[2026-10-06] eagle: work/projects/vscode-migration/modularization-roadmap.md work/projects/vscode-migration/vscode-migration.md
This commit is contained in:
1 parent
4a0ec272f0
commit
5b80ca8893
2 files changed
+84
-10
No files matched your search
@@ -14,17 +14,16 @@ Related: [[projects/vscode-migration/vscode-migration]] · [[projects/vscode-mig
|
||||
- `GeneralPixel`: 115 files;
|
||||
- `Preferences`: it holds both the settings models and the Settings UI;
|
||||
- `Tab` with `TabExtensions`: every feature's tab extension is registered in one place.
|
||||
- Proposed order:
|
||||
- Proposed order (detailed in [Part 4](#part-4-roadmap)):
|
||||
1. Foundation modules.
|
||||
2. Contracts and dependency injection.
|
||||
3. Settings split.
|
||||
4. Leaf features (can start at once).
|
||||
5. Tab core.
|
||||
6. Mid-tier features.
|
||||
7. Shell.
|
||||
2. Contracts and dependency injection, including the settings split.
|
||||
3. Leaf features (can start right after the first foundation module).
|
||||
4. Tab core.
|
||||
5. Mid-tier features.
|
||||
6. Shell.
|
||||
|
||||
Each step is a series of small PRs, and the app keeps building at every step.
|
||||
- The sharing candidates with iOS are listed in [Cross-platform packages](#cross-platform-packages-ios--macos).
|
||||
- iOS has the same problem: 1,450 files and ~273k lines in its app target, almost all of it outside packages. About **15 areas** have near-duplicate logic on both platforms; several files are 90–99% identical. Sharing these now removes duplication and makes the macOS extractions cheaper. See [Part 5](#part-5-cross-platform-packages-ios--macos).
|
||||
|
||||
---
|
||||
|
||||
@@ -195,7 +194,7 @@ Rules:
|
||||
|
||||
## Part 4: Roadmap
|
||||
|
||||
Each phase is a set of small PRs, and the app builds and the tests pass after each one. Phases 1–3 mostly break dependencies, and files move into new targets as each folder gets clean. Phase 4 can run in parallel with Phase 1.
|
||||
Each phase is a set of small PRs, and the app builds and the tests pass after each one. Phases 1, 2 and 4 mostly break dependencies; files move into new targets once a folder is clean. Phase 3 (leaf features) and the "share now" items from Part 5 can run in parallel with Phases 1–2.
|
||||
|
||||
### Phase 1: Foundation modules (break the lowest cycle)
|
||||
|
||||
@@ -207,6 +206,7 @@ Each phase is a set of small PRs, and the app builds and the tests pass after ea
|
||||
| 1.4 | Strings | `UserText` (1,980 keys) | Set the pattern: each module has its own `Localizable.xcstrings` and a `UserText` extension. A script moves keys to the module that uses them; common ones (OK, Cancel, …) go to `BrowserDesignSystem`. Check `loc_export.sh` and the translation import with several catalogs. This unblocks **E2 + H** | tooling S, then per feature |
|
||||
| 1.5 | `BrowserStatistics` | `Statistics/ATB`, `StatisticsLoader`, `LocalStatisticsStore`, pixel helpers | Split `GeneralPixel` per feature; per-feature pixel enums already exist for History, NTP, Settings, Permissions and others. Remove the back references to `MainWindow`, `Menus`, `Permissions`, `Preferences`, `Onboarding` | ~7k lines, M |
|
||||
| 1.6 | Favicons, Fireproofing, UnprotectedDomains | Data services that many features need | `Favicons` → `BookmarkManager` goes through a protocol. Each keeps its Core Data model | ~4k lines, S |
|
||||
| 1.7 | Database per module | `Common/Database/Database.swift` builds **one encrypted store** from every model in `Bundle.module` (7 app models + `HTTPSUpgrade`) and registers the shared value transformers | Make `Database` take the list of models from its callers, so each module can bring its own `.xcdatamodeld` in its own bundle. Keep one store file, so no data migration is needed. The encryption transformers go to `BrowserFoundation`. This is a prerequisite for moving any feature that owns a model: Bookmarks (legacy), History, Favicons, Fireproofing, Permissions, Downloads, pixel state, unprotected domains | S–M |
|
||||
|
||||
Exit criterion: the foundation modules have no cycle and import no feature code.
|
||||
|
||||
@@ -298,7 +298,80 @@ Add these checks to a script and run it in CI:
|
||||
|
||||
## Part 5: Cross-platform packages (iOS + macOS)
|
||||
|
||||
_(see below)_
|
||||
### Where iOS stands
|
||||
|
||||
- `iOS/DuckDuckGo` has **1,450 Swift files and ~273k lines** in the app target, 294 of them flat in the root folder. Another 62 files (~11k lines) are in `iOS/Core`, which is an Xcode framework, not SwiftPM.
|
||||
- The 10 iOS local packages are small (~21k lines in total) and declared iOS-only. Two of them already have a clean Core/UI split: `SitePermissions` (5.1k) and `SetDefaultBrowser` (2.4k).
|
||||
- So both apps are in the same place. A shared package removes code from **two** monoliths at once.
|
||||
|
||||
### What is already shared
|
||||
|
||||
- **BrowserServicesKit** products, used by both apps:
|
||||
- Bookmarks (+ `BookmarksModel`), History (+ `BrowsingHistory`), Suggestions;
|
||||
- Subscription, DDGSync / SyncDataProviders, RemoteMessaging;
|
||||
- PrivacyConfig, PrivacyDashboard, ContentBlocking, MaliciousSiteProtection;
|
||||
- Autofill/SecureVault, DataImport (CSV, cards), DuckPlayer (0.3k), Statistics protocols, Configuration, UserScript.
|
||||
- **Standalone shared packages:** AIChat, Onboarding, WebExtensions, VPN, DataBrokerProtectionCore, EventHub, UIComponents, AttributedMetric, PixelKit, Persistence (incl. `KeyedStoring`), Networking, Common, WideEvent, SERPSettings.
|
||||
- **The pattern to follow:** `SharedPackages/AIChat` keeps `Shared/` (91 files), `iOS/` (16) and `macOS/` (2) in one package. `WebExtensions` uses `+iOS` / `+macOS` adapters.
|
||||
|
||||
### What is duplicated
|
||||
|
||||
Similarity = 1 − (differing lines / total lines of both files), with license headers stripped and whitespace ignored. Spot-checked: `TipKitController` differs by 1 line, `UnifiedMetadataCollector` by 3, `SyncMetricsEventsHandler` by 6, `ImportArchiveReader` by 14 of 138.
|
||||
|
||||
| Area | Near-duplicate files on both platforms | Destination | Verdict |
|
||||
|---|---|---|---|
|
||||
| **Sync app integration** | `SyncDataProviders` 70%, `SyncErrorHandler` 57%, `SyncBookmarksAdapter` 67%, `SyncCredentialsAdapter` 77%, `SyncCreditCardsAdapter` 83%, `SyncSettingsAdapter` 81%, `SyncMetadataDatabase` 72%, `SyncMetricsEventsHandler` 92%, cleanup error handlers 90–92%, `UnifiedDeviceListPixelHandler` 92%, `AIChatDeleteOperation` 95%, `FavoritesDisplayModeSyncHandler` 76%, `SyncPromoViewModel` 76% | new shared `SyncAppIntegration` package (or a BSK product) | **Share now.** ~3k duplicated lines; the biggest win |
|
||||
| **Content blocking providers + ad blocking** | `ContentBlockerRulesLists` 81%, `AppTrackerDataSetProvider` 82%, `AppPrivacyConfigurationDataProvider` 75%, `AdBlockingAvailability` 92%, `AdBlockingNavigationHandler` 81% | BSK `ContentBlocking` / `PrivacyConfig` | **Share now.** The embedded config file is a parameter. The `ContentBlocking` composition root is shared after refactor |
|
||||
| **Autoconsent (cookie popups)** | `AutoconsentUserScript` (779 / 746 lines, 80%), `AutoconsentPixels` 81%, `AutoconsentManagement` 37% | `WebExtensions/Autoconsent` or a BSK product | **Share now.** Settings and feature flags go behind protocols |
|
||||
| **Feedback** | `UnifiedMetadataCollector` 97%, `UnifiedFeedbackFormViewModel` 72%, `FeedbackCategoryProviding` 72%, `UnifiedFeedbackSender` 47%, VPN/DBP metadata collectors 61–65% | new shared `Feedback` package (model + sender), UI per platform | **Share now** |
|
||||
| **TipKit** | `TipKitController` 99% + 4 files at 89–99% | `UIComponents` or a new `TipKitSupport` | **Share now.** Also resolves part of stage E1 |
|
||||
| **Data import** | `ImportArchiveReader` 94% (copy-paste), `AutofillLoginImportState` 60%; bookmark HTML parsing implemented twice (XMLParser on macOS, SwiftSoup on iOS) | BSK `DataImport` | **Share now** for the archive reader, HTML parser and import state. Browser-profile readers stay on macOS |
|
||||
| **Duck.ai** | `DuckAiUsageWarningPixel` 90%, `AIChatSuggestionsReader` 84%, `AIChatFeatureFlagProvider` 83%, `PageContextExtractionPixelHandler` 80%, `PageContextUserScript` 63%, `DuckAiUsageLimitsStore` 51%; user-script handlers 35% | `AIChat/Shared` | **Share now** for stores, pixels and the suggestions reader. The user-script handler is shared after refactor |
|
||||
| **Data clearing telemetry** | `DataClearingWideEventService` 64%, `DataClearingPixelsReporter` 50% | BSK `DataClearing` | **Share now.** The Fire engine is shared after refactor; iOS's worker-based `FireExecutor` is the better base than macOS's 1,324-line `Fire.swift` |
|
||||
| **Remote messaging glue** | `DefaultRemoteMessagingSurfacesProvider` 92%, `OSUpgradeCapabilityOverride` 95%, store error handling 75%, matcher provider 55% | BSK `RemoteMessaging` | **Share now** (small) |
|
||||
| **Small managers** | `AutofillNeverPromptWebsitesManager` 91%, `EmailManagerRequestDelegate` 76%, `SecureVaultReporter` 67%, `PrivacyStatsDatabase` 53%, `CrashReportSenderExtensions` 83%, `UserScriptError+Pixel` 87% | the matching BSK products | **Share now** (tiny PRs) |
|
||||
| **Subscription web flows** | `SubscriptionPagesUserScript` 81%, `SubscriptionPagesUseSubscriptionFeature` (~1k lines each side, 40%), feature-flag adapters 83–95%, `SubscriptionPixelHandler` 76%, `FreeTrialPixelHandler` 85%, `SubscriptionEnvironment+Default` 80%, `VPNSettings+Environment` 92% | BSK `Subscription` with a `SubscriptionUIHandling` protocol | **Share after refactor.** The small adapters can be shared now. They are also stage **D**, so a shared home solves D for the helpers too |
|
||||
| **Statistics / ATB** | `StatisticsLoader` 52%, `AtbParser` 85%, `AtbAndVariantCleanup` 86%, `StatisticsStore` 68%, `VariantManager` ~53% | BSK `Statistics` | **Share after refactor.** Storage goes through `KeyedStoring`. Pixel enums stay per platform |
|
||||
| **Bookmarks** | DB setup, `BookmarksCleanupErrorHandling` 93%, `BookmarksExporter` 68%, legacy migration 52%. macOS has its own `LocalBookmarkStore` (1,314 lines) + `BookmarkManager` (841) on top of the shared model; iOS uses the BSK view models directly | BSK `Bookmarks` | **Share after refactor.** Move the store logic down, keep the tree/outline/pasteboard UI on macOS. `Bookmark` must stop holding `NSImage` and calling `faviconManager` |
|
||||
| **History** | macOS `EncryptedHistoryStore` (463 lines) re-implements BSK `HistoryStore` (428) on its own encrypted `History.xcdatamodeld`; the schemas match except for encryption and entity names | BSK `History` with a pluggable encryption transformer | **Share after refactor.** Needs a macOS data migration |
|
||||
| **Permissions** | Same domain, different designs: macOS `PermissionManager` + Core Data store (7 permission types); iOS `SitePermissions` package (KeyedStoring, 3 types) | make `SitePermissions` multi-platform, add the macOS types | **Share after refactor.** Needs a migration of macOS data |
|
||||
| **Fireproofing** | Same API (`isAllowed(cookieDomain:)`, eTLD+1 migration); macOS stores in Core Data, iOS in UserDefaults | new shared `Fireproofing` with injected storage | **Share after refactor** (small, low risk) |
|
||||
| **Duck Player** | `YoutubeOverlayUserScript` 53%, `YoutubePlayerUserScript` 51%; macOS code has no AppKit | BSK `DuckPlayer` | **Share after refactor** |
|
||||
| **Default browser prompts** | prompt type deciders 45–60%, `UserActivity` model ~80% | make `SetDefaultBrowserCore` multi-platform, add the macOS dock prompt | **Share after refactor** |
|
||||
| **Configuration, MaliciousSiteProtection** | `ConfigurationManager` 55%, `AppConfigurationURLProvider` 67%; MSP manager 28% | BSK `Configuration` / `MaliciousSiteProtection` | **Share after refactor** (small) |
|
||||
|
||||
**Keep platform-specific:**
|
||||
- Tab model: ~10% similar, different designs.
|
||||
- Downloads: Core Data + `WebKitDownloadTask` on macOS, filesystem + QuickLook on iOS.
|
||||
- Favicons: Core Data on macOS, Kingfisher on iOS.
|
||||
- New Tab Page: web NTP on macOS, native on iOS.
|
||||
- VPN app glue: system extension, IPC, login items.
|
||||
- Freemium and Updates/Sparkle: macOS only.
|
||||
- Feature flag enums: 175 macOS cases vs 189 iOS cases, 58 names on both. Share a provider protocol, not the enum.
|
||||
- `UserDefaultsWrapper` (each app has its own): migrate both to `KeyedStoring`; don't share it.
|
||||
|
||||
### How sharing fits the macOS roadmap
|
||||
|
||||
- **Do the "share now" items before or during Phase 3.** They are small PRs, mostly into existing BSK products, and they shrink both monoliths. Each one also removes a macOS file that would otherwise need a new home later.
|
||||
- **"Share after refactor" items replace a Phase 5 extraction.** When Bookmarks, History, Permissions or Statistics reach Phase 5, the logic goes into the shared package instead of a new macOS module. The macOS module keeps only UI and glue, which is the pattern `SyncUI-macOS`, `HistoryView` and `NewTabPage` already follow.
|
||||
- **Shared prerequisites, needed on both platforms:**
|
||||
- `KeyedStoring` instead of `UserDefaultsWrapper`;
|
||||
- settings and feature-flag access through protocols, not app singletons;
|
||||
- per-module string catalogs;
|
||||
- no `NSImage`/`UIImage` in model types;
|
||||
- on macOS, the per-module database (step 1.7).
|
||||
- **Package layout:** one package per domain with `Shared/`, `macOS/` and `iOS/` folders (the AIChat pattern), or BSK products for model and logic only. UI never goes into `Shared/`.
|
||||
|
||||
### Suggested first cross-platform PRs (each small and independent)
|
||||
|
||||
1. TipKit → shared (5 files, ~99% identical).
|
||||
2. `UnifiedMetadataCollector` + unified feedback model and sender → shared `Feedback`.
|
||||
3. `ImportArchiveReader` → BSK `DataImport`.
|
||||
4. Sync adapters, metrics handler and cleanup error handlers → `SyncAppIntegration`.
|
||||
5. Content-blocking data providers + `AdBlockingAvailability` → BSK.
|
||||
6. `AutoconsentUserScript` + pixels → shared.
|
||||
7. Duck.ai stores and pixels → `AIChat/Shared`.
|
||||
8. Subscription environment and pixel adapters → shared subscription support. This also closes stage **D**.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Status: **IN PROGRESS** · updated 2026-10-04
|
||||
**Asana:** [Migration to fully VSCode-supported builds&debugging](https://app.asana.com/1/137249556945/project/1203301625297703/task/1209808486380252)
|
||||
**Repo:** `~/DuckDuckGo/apple-browsers.git` (bare + worktrees)
|
||||
**Findings:** [[projects/vscode-migration/findings]]
|
||||
**Next:** [[projects/vscode-migration/modularization-roadmap]] — modularization after the browser package (incl. iOS/macOS sharing)
|
||||
|
||||
## Goal
|
||||
|
||||
|
||||
Reference in new issue
Block a user