Desktop & Web Distribution: Microsoft Store, Mac App Store, PWA, and Beyond

Desktop & Web Distribution: Microsoft Store, Mac App Store, PWA, and Beyond

Complete guide to distributing desktop and web apps: Microsoft Store MSIX, Mac App Store, macOS notarization, Electron packaging, PWA publishing, and web deployment pipelines.

By Omar Flores

Mobile distribution has Google Play and the App Store — two stores, two pipelines, well-documented paths. Desktop and web distribution is a different landscape: five or six overlapping channels, each with its own signing requirements, update mechanisms, and review processes. A developer shipping a cross-platform desktop app in 2026 needs to understand Windows MSIX packaging, macOS notarization, Electron auto-update, PWA installability, and web deployment — all at the same time.

This post covers each channel from the perspective of what a developer actually needs to do: the accounts, the tooling, the signing configuration, the submission process, and the update strategy. No single post can be exhaustive on every platform, but this one is complete enough to get a production app through each distribution path without hitting the most common obstacles.


The Distribution Landscape

Before the mechanics, it helps to see the full picture of what you are choosing between:

ChannelOSReviewUpdate mechanismCost
Microsoft StoreWindowsYesStore or MSIX deltaFree account, $19 one-time
Mac App StoremacOSYesApp Store$99/year (shared with iOS)
macOS Direct (notarized)macOSNo (Gatekeeper check)Custom (Sparkle, etc.)$99/year (cert)
Windows Direct (signed)WindowsNoSquirrel / customCode signing cert required
Electron + GitHub ReleasesBothNoelectron-updaterFree
PWAAny (browser)NoService workerFree
Web (Netlify/Vercel/etc.)BrowserNoDeploy pipelineFree tier available

Most desktop apps use more than one: the store for discoverability, direct download for users who prefer it, and auto-update so existing users get new versions without going back to the store.


Microsoft Store

Account Setup

Create a developer account at partner.microsoft.com/dashboard. Individual accounts cost $19 one-time. Company accounts cost $99 one-time and require a D-U-N-S number for verification — similar to Apple’s requirement.

The Microsoft Store allows both Win32 apps and UWP apps distributed as MSIX packages. Since Windows 10 1903, you can package a traditional Win32 application as MSIX without rewriting it. This is the path for most desktop developers coming from existing Win32 apps.

MSIX Packaging

MSIX is Microsoft’s modern packaging format. It provides clean install/uninstall, automatic updates via the store, and a sandboxed installation directory. For apps distributed outside the store, it also enables delta updates.

Creating an MSIX package with msixpackaging or Visual Studio:

For an Electron or generic Win32 app, use the MSIX Packaging Tool (from the Microsoft Store on Windows) or electron-builder with the appx target.

For a native .NET / WPF / WinForms app in Visual Studio:

<!-- Right-click project → Add → Publishing → Package (Windows Application Packaging Project) -->
<!-- Or add a Windows Application Packaging Project to the solution -->

<!-- Package.appxmanifest -->
<Identity
  Name="YourCompany.YourApp"
  Publisher="CN=YourCN, O=YourCompany, L=City, S=State, C=US"
  Version="2.1.0.0" />

<Properties>
  <DisplayName>Your App</DisplayName>
  <PublisherDisplayName>Your Company</PublisherDisplayName>
  <Logo>Assets\StoreLogo.png</Logo>
</Properties>

<Capabilities>
  <Capability Name="internetClient" />
  <!-- add only what the app actually needs -->
</Capabilities>

The Publisher value in the manifest must match the publisher CN on your code signing certificate. If submitting to the Microsoft Store, it must match the publisher identity in your Partner Center account.

Building the MSIX:

# With msbuild
msbuild YourApp.sln /p:Configuration=Release /p:Platform=x64 /p:AppxBundle=Always

# With dotnet
dotnet publish -c Release -r win-x64 --self-contained true

Code Signing for Windows

For direct distribution (outside the store), your MSIX or installer must be signed with a trusted code signing certificate. Unsigned installers trigger a SmartScreen warning that causes many users to abandon the install.

Options for code signing certificates:

  • EV (Extended Validation) certificate — immediately trusted, no SmartScreen warning, more expensive (~$300–500/year), requires hardware token
  • OV (Organization Validation) certificate — trusted after reputation is built through user downloads, cheaper (~$100–200/year)
  • Azure Trusted Signing — Microsoft’s new cloud-based signing service, lower cost, no hardware token required, SmartScreen trust builds automatically

Signing with signtool (included in Windows SDK):

# Sign the MSIX
signtool sign `
  /fd SHA256 `
  /a `
  /f certificate.pfx `
  /p $env:CERT_PASSWORD `
  /tr http://timestamp.digicert.com `
  /td SHA256 `
  YourApp.msix

# Verify the signature
signtool verify /pa /v YourApp.msix

Always include a timestamp (/tr) when signing. Without it, the signature becomes invalid when the certificate expires, even for already-distributed files.

Microsoft Store Submission

In Partner Center:

  1. Create a new app — reserve the app name. The name is reserved globally across all Windows devices once you reserve it.
  2. Age ratings — complete the IARC questionnaire (same system as Google Play).
  3. Properties — category, subcategory, support contact, privacy policy URL.
  4. Store listing — description (up to 10,000 characters), features list (up to 20 items, 200 chars each), screenshots (minimum 1, recommended 4–8 at 1366×768 or higher), app tile icon (300×300px).
  5. Packages — upload the MSIX file. Partner Center validates the manifest.
  6. Submission — review typically takes 1–5 business days.

Common rejection reasons:

  • Manifest capabilities that do not match actual app behavior (requesting broadFileSystemAccess for an app that reads one specific folder)
  • App crashes on launch — Microsoft runs automated testing
  • Missing privacy policy for apps that connect to the internet
  • Screenshots that do not represent the actual application
  • App that is just a web wrapper with no added value (web-only apps belong in the PWA path)

Windows Direct Distribution (Outside Store)

For users who prefer direct downloads, provide a signed MSIX or a signed .exe installer. The two common installer frameworks:

Inno Setup — lightweight, scriptable, widely used for Win32 apps:

[Setup]
AppName=Your App
AppVersion=2.1.0
AppPublisher=Your Company
DefaultDirName={autopf}\YourApp
DefaultGroupName=YourApp
OutputBaseFilename=YourApp-Setup-2.1.0
Compression=lzma2
SolidCompression=yes
SignTool=signtool sign /fd SHA256 /a /f "cert.pfx" /p "$ENV:CERT_PASSWORD" /tr http://timestamp.digicert.com /td SHA256 $f

[Files]
Source: "dist\*"; DestDir: "{app}"; Flags: recursesubdirs

[Icons]
Name: "{group}\YourApp"; Filename: "{app}\YourApp.exe"
Name: "{commondesktop}\YourApp"; Filename: "{app}\YourApp.exe"

[Run]
Filename: "{app}\YourApp.exe"; Description: "Launch YourApp"; Flags: nowait postinstall skipifsilent

WiX Toolset — for MSI-based installers, required for some enterprise deployment systems.


Mac App Store

Distribution Options on macOS

macOS has two distinct distribution paths that require the same Apple Developer Program membership ($99/year, shared with iOS):

Mac App Store — reviewed by Apple, installed via the App Store, sandboxed. More restrictive but more trusted and discoverable.

Developer ID — signed and notarized by Apple but distributed directly (your website, GitHub releases, etc.). No sandbox requirement, no review, but requires notarization for Gatekeeper to allow installation.

Most professional desktop apps use both: the Mac App Store for users who prefer it, and a notarized direct download .dmg for users who need capabilities that the sandbox restricts (accessing arbitrary file system paths, running other processes, etc.).

Mac App Store Submission

The Mac App Store uses the same App Store Connect infrastructure as iOS. The differences:

  • The build is a macOS application bundle (.app) archived and uploaded via Xcode or Transporter
  • Sandbox entitlements are required — the app must declare what it needs (com.apple.security.network.client, com.apple.security.files.user-selected.read-write, etc.)
  • Screenshots must be 1280×800 or 1440×900
  • The app must not contain auto-update code that bypasses the Mac App Store update mechanism

Entitlements for a Mac App Store build:

<!-- YourApp.entitlements (App Store distribution) -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- required for all Mac App Store apps -->
    <key>com.apple.security.app-sandbox</key>
    <true/>

    <!-- network access -->
    <key>com.apple.security.network.client</key>
    <true/>

    <!-- read files the user selects -->
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>

    <!-- if the app uses iCloud -->
    <key>com.apple.security.application-groups</key>
    <array>
        <string>$(TeamIdentifierPrefix)com.yourcompany.yourapp</string>
    </array>
</dict>
</plist>

Build and archive from Xcode, then upload with Transporter or xcrun altool.

Notarization for Direct Distribution

For apps distributed outside the Mac App Store, notarization is required on macOS 10.15 and later. Without it, Gatekeeper blocks the app with a warning that users cannot easily bypass.

Notarization is Apple’s automated malware scan. You submit the binary, Apple scans it, and returns a ticket. You then staple the ticket to the app so it can be verified offline.

The notarization workflow:

# 1. Build and sign with Developer ID certificate
codesign --deep --force --verify --verbose \
  --sign "Developer ID Application: Your Company (TEAMID)" \
  --options runtime \
  --entitlements YourApp.entitlements \
  YourApp.app

# 2. Create a DMG or ZIP for notarization
hdiutil create -volname "YourApp" -srcfolder YourApp.app \
  -ov -format UDZO YourApp.dmg

# 3. Submit for notarization
xcrun notarytool submit YourApp.dmg \
  --apple-id "you@example.com" \
  --password "$APP_SPECIFIC_PASSWORD" \
  --team-id "TEAMID" \
  --wait

# 4. Staple the ticket to the DMG
xcrun stapler staple YourApp.dmg

# 5. Verify
xcrun stapler validate YourApp.dmg
spctl --assess --verbose --type install YourApp.dmg

Use an app-specific password (not your Apple ID password) for notarytool. Generate it at appleid.apple.com under App-Specific Passwords.

Hardened Runtime (--options runtime in codesign) is required for notarization. It enforces security restrictions similar to a subset of the sandbox. If your app uses dynamic libraries, JIT compilation, or certain system resources, you may need to add exceptions in the entitlements:

<!-- YourApp.entitlements (Developer ID / notarization) -->
<dict>
    <!-- if the app uses a JIT compiler (e.g., Electron's V8) -->
    <key>com.apple.security.cs.allow-jit</key>
    <true/>

    <!-- if the app loads unsigned libraries -->
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>

Only add exceptions you actually need. Each one is a potential security surface that Apple’s notarization service evaluates.


Electron: Cross-Platform Desktop

Electron apps run on Windows, macOS, and Linux from a single codebase. The distribution complexity comes from needing to sign, package, and update for each platform independently.

electron-builder

electron-builder is the standard packaging and distribution tool for Electron apps. It handles MSIX/NSIS for Windows, DMG/PKG for macOS, AppImage/deb/rpm for Linux, and integrates with auto-update.

npm install --save-dev electron-builder

package.json build configuration:

{
  "build": {
    "appId": "com.yourcompany.yourapp",
    "productName": "Your App",
    "copyright": "Copyright © 2026 Your Company",
    "directories": {
      "output": "dist"
    },
    "files": [
      "dist/**/*",
      "node_modules/**/*",
      "package.json"
    ],
    "win": {
      "target": [
        { "target": "nsis", "arch": ["x64", "arm64"] },
        { "target": "appx", "arch": ["x64"] }
      ],
      "certificateFile": "cert.pfx",
      "certificatePassword": "${env.CERT_PASSWORD}",
      "publisherName": "Your Company",
      "timeStampServer": "http://timestamp.digicert.com"
    },
    "mac": {
      "target": [
        { "target": "dmg", "arch": ["x64", "arm64"] },
        { "target": "zip", "arch": ["x64", "arm64"] }
      ],
      "category": "public.app-category.productivity",
      "identity": "Developer ID Application: Your Company (TEAMID)",
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "build/entitlements.mac.plist",
      "entitlementsInherit": "build/entitlements.mac.plist",
      "notarize": {
        "teamId": "TEAMID"
      }
    },
    "linux": {
      "target": ["AppImage", "deb"],
      "category": "Utility"
    },
    "publish": {
      "provider": "github",
      "owner": "yourorg",
      "repo": "yourapp"
    }
  }
}

Build for all platforms:

# current platform only
npx electron-builder build

# specific platform (requires the platform's tooling)
npx electron-builder build --win
npx electron-builder build --mac
npx electron-builder build --linux

Building macOS installers from a Linux/Windows CI requires a macOS runner. GitHub Actions has macos-latest available.

Auto-Update with electron-updater

electron-updater (part of electron-builder) implements auto-update using GitHub Releases, S3, or a custom server as the update source.

npm install electron-updater

In the main process:

// main/updater.js
const { autoUpdater } = require('electron-updater')
const { app, dialog } = require('electron')
const log = require('electron-log')

autoUpdater.logger = log
autoUpdater.logger.transports.file.level = 'info'

// check for updates after the window is ready
function initAutoUpdater() {
  autoUpdater.checkForUpdatesAndNotify()

  autoUpdater.on('update-available', (info) => {
    log.info('Update available:', info.version)
  })

  autoUpdater.on('update-downloaded', (info) => {
    dialog.showMessageBox({
      type: 'info',
      title: 'Update Ready',
      message: `Version ${info.version} has been downloaded. Restart to install.`,
      buttons: ['Restart Now', 'Later']
    }).then(({ response }) => {
      if (response === 0) autoUpdater.quitAndInstall()
    })
  })

  autoUpdater.on('error', (err) => {
    log.error('Auto-updater error:', err)
  })
}

module.exports = { initAutoUpdater }

The publish config in package.json points to a GitHub repository. When you create a GitHub Release with the built artifacts, electron-updater fetches the latest.yml (Windows) or latest-mac.yml (macOS) file from the release assets and compares versions.

GitHub Actions for Electron Releases

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    strategy:
      matrix:
        os: [macos-latest, windows-latest, ubuntu-latest]
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - run: npm ci

      - name: Build (macOS)
        if: matrix.os == 'macos-latest'
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          CSC_LINK: ${{ secrets.MAC_CERT_BASE64 }}
          CSC_KEY_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }}
        run: npx electron-builder build --mac --publish always

      - name: Build (Windows)
        if: matrix.os == 'windows-latest'
        env:
          CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
        run: |
          echo "${{ secrets.WIN_CERT_BASE64 }}" | base64 -d > cert.pfx
          npx electron-builder build --win --publish always

      - name: Build (Linux)
        if: matrix.os == 'ubuntu-latest'
        run: npx electron-builder build --linux --publish always

The --publish always flag uploads built artifacts directly to the GitHub Release. The workflow triggers on version tags (v2.1.0), which you create when ready to release:

git tag v2.1.0
git push origin v2.1.0

Progressive Web Apps (PWA)

A PWA is a web app that uses browser APIs to provide an app-like experience: installable on the home screen or taskbar, offline capability, push notifications, and background sync. Distribution requires no store submission — users install directly from the browser.

PWA Requirements

For a web app to be installable as a PWA, it must meet these criteria in Chrome/Edge (other browsers have similar requirements):

  1. Served over HTTPS
  2. Has a valid Web App Manifest
  3. Has a registered Service Worker with a fetch event handler
  4. The manifest has start_url, icons (at least 192×192 and 512×512), display: standalone or fullscreen
  5. Not already installed

Web App Manifest (/manifest.json or /manifest.webmanifest):

{
  "name": "Your Application",
  "short_name": "YourApp",
  "description": "What your app does, used in app stores",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196f3",
  "orientation": "any",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ],
  "screenshots": [
    {
      "src": "/screenshots/desktop.png",
      "sizes": "1280x720",
      "type": "image/png",
      "form_factor": "wide"
    },
    {
      "src": "/screenshots/mobile.png",
      "sizes": "390x844",
      "type": "image/png",
      "form_factor": "narrow"
    }
  ],
  "shortcuts": [
    {
      "name": "New Task",
      "url": "/tasks/new",
      "icons": [{ "src": "/icons/new-task.png", "sizes": "96x96" }]
    }
  ]
}

Link it in your HTML <head>:

<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#2196f3" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />

Service Worker — the minimum viable service worker for installability:

// public/sw.js
const CACHE_NAME = 'v2.1.0'
const STATIC_ASSETS = [
  '/',
  '/manifest.json',
  '/icons/icon-192.png',
  '/icons/icon-512.png'
]

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
  )
  self.skipWaiting()
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
    )
  )
  self.clients.claim()
})

self.addEventListener('fetch', (event) => {
  // network first for API calls, cache first for static assets
  if (event.request.url.includes('/api/')) {
    event.respondWith(
      fetch(event.request).catch(() => caches.match(event.request))
    )
  } else {
    event.respondWith(
      caches.match(event.request).then((cached) => cached || fetch(event.request))
    )
  }
})

Register the service worker in your app entry point:

// main.js or index.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then((reg) => console.log('SW registered:', reg.scope))
      .catch((err) => console.error('SW registration failed:', err))
  })
}

Handling the Install Prompt

Browsers fire the beforeinstallprompt event before showing the install UI. You can intercept it and show your own install button at the right moment:

let deferredPrompt

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault()
  deferredPrompt = e
  // show your custom install button
  document.getElementById('install-btn').style.display = 'block'
})

document.getElementById('install-btn').addEventListener('click', async () => {
  if (!deferredPrompt) return
  deferredPrompt.prompt()
  const { outcome } = await deferredPrompt.userChoice
  console.log('Install outcome:', outcome) // 'accepted' or 'dismissed'
  deferredPrompt = null
  document.getElementById('install-btn').style.display = 'none'
})

window.addEventListener('appinstalled', () => {
  console.log('App installed')
  deferredPrompt = null
})

Show the install button after the user has interacted with the app meaningfully — not on first load. The browser will not fire beforeinstallprompt again if the user dismisses it without a timeout, so the timing matters.

Microsoft Edge Add-ons Store (PWA)

Microsoft Edge supports submitting PWAs to the Microsoft Store via the Edge Add-ons program. This gives your PWA a store presence on Windows without an MSIX package.

In Partner Center, create a new app with the PWA category. Provide your HTTPS URL — Microsoft crawls it, validates the manifest and service worker, and creates a listing. Users can install directly from the store and it appears in the Windows taskbar like a native app.

Requirements:

  • Valid HTTPS URL
  • Web App Manifest with required fields
  • Service worker registered and functional
  • Privacy policy URL

Web Deployment Pipelines

For web apps and PWAs, the deployment pipeline is the distribution mechanism. Every push to the production branch is a release.

Netlify

Netlify is the most common hosting platform for static and JAMstack sites. It handles CDN, SSL, branch previews, and form handling.

# netlify.toml
[build]
  command = "bun run build"
  publish = "dist"
  environment = { NODE_VERSION = "22" }

[build.environment]
  ASTRO_TELEMETRY_DISABLED = "1"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200    # SPA fallback

[[headers]]
  for = "/manifest.json"
  [headers.values]
    Content-Type = "application/manifest+json"
    Cache-Control = "public, max-age=86400"

[[headers]]
  for = "/sw.js"
  [headers.values]
    Cache-Control = "no-cache"    # always fetch fresh service worker

[[headers]]
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[context.production]
  command = "bun run build"

[context.deploy-preview]
  command = "bun run build"

The service worker must be served with no-cache — if the browser caches the service worker file, users do not receive updates. Hashed asset filenames (.js?hash=abc123) can be cached aggressively; the SW itself cannot.

Vercel

Vercel is optimized for Next.js but works with any framework. Configuration via vercel.json:

{
  "buildCommand": "bun run build",
  "outputDirectory": "dist",
  "framework": "astro",
  "headers": [
    {
      "source": "/sw.js",
      "headers": [{ "key": "Cache-Control", "value": "no-cache" }]
    },
    {
      "source": "/(.*)\\.js",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
    }
  ],
  "rewrites": [
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}

Deploy via CLI or GitHub integration:

# install
npm i -g vercel

# deploy to preview
vercel

# deploy to production
vercel --prod

Custom Domains and SSL

Both Netlify and Vercel handle SSL automatically via Let’s Encrypt. For custom domains:

# Netlify CLI
netlify domains:add yourdomain.com

# Then add CNAME record in your DNS:
# www.yourdomain.com → your-site.netlify.app
# Root domain: use Netlify DNS or ALIAS record

For a PWA, the domain is permanent — changing it breaks all existing installations. Choose your production domain before you launch and treat it as infrastructure, not config.

Rollback Strategy

Every deployment should be reversible in under 60 seconds. Both Netlify and Vercel keep deployment history and allow instant rollback:

# Netlify
netlify rollback

# Vercel
vercel rollback <deployment-url>

For self-hosted deployments, use blue-green or tag-based rollback:

# Docker + nginx blue-green
docker pull yourregistry/yourapp:v2.1.0
docker stop app-green
docker run -d --name app-green -p 3001:3000 yourregistry/yourapp:v2.1.0
# health check
curl -f http://localhost:3001/health || exit 1
# switch nginx upstream
nginx -s reload
# stop old container
docker stop app-blue

Versioning Across Channels

When distributing across multiple channels, version numbers must be consistent and meaningful to users — even if the internal build numbers differ per platform.

Use semantic versioning (MAJOR.MINOR.PATCH) for the user-visible version across all channels:

2.1.0 → same string in:
  - package.json "version"
  - Windows MSIX Identity Version (2.1.0.0 — fourth component must be 0)
  - macOS CFBundleShortVersionString
  - Android versionName
  - iOS CFBundleShortVersionString
  - Web manifest "version" (if included)

The internal build counters (versionCode, CFBundleVersion, Windows build number) can differ per platform — they are machine-readable monotonic counters, not the user-facing version.

Tag releases in git with the user-facing version:

git tag v2.1.0
git push origin v2.1.0

Your CI pipeline triggers on this tag and builds all platform artifacts simultaneously, uploading each to its respective distribution channel.


Update Communication

Users need to know when updates are available. The mechanics differ per channel — store apps update automatically or with a notification, direct-download apps need an explicit update mechanism — but the communication principle is the same: tell users what changed, why it matters, and whether a restart is required.

Release notes written for users, not developers:

Version 2.1.0

New: Calendar view for tasks — see your week at a glance on the Today screen
New: Recurring tasks — set daily, weekly, or monthly repeats from the task form
Improved: Search now finds tasks by description, not just title
Fixed: Tasks with long titles no longer overflow the card in the list view
Fixed: App correctly restores last open project after restart on Windows

Each line starts with the user impact (New, Improved, Fixed), not the technical detail. The internal commit “fix: truncate title at 48 chars in TaskCard component” becomes “Tasks with long titles no longer overflow the card.”


Distribution is infrastructure. It is the layer between the code you write and the people who use it. Every platform in this post — the Microsoft Store, the Mac App Store, Electron auto-update, PWA service workers, Netlify deployments — has its own maintenance cost and its own failure modes. The developers who handle this well are the ones who automated the repetitive parts, set up monitoring for each channel, and built rollback into the process before they needed it.

Shipping is not the end of the work. It is the moment the work becomes real. Build the pipeline before you need it, and maintain it like the infrastructure it is.

Tags

#devops #tutorial #guide #best-practices #tips #frontend