Back to Wekan

Less Code

docs/Design/LessCode.md

11.4814.3 KB
Original Source

Less Code

This document records a measured plan for reducing WeKan's maintained source code without removing supported behaviour, translations, accessibility or tests. Fewer lines are useful only when they also leave fewer independent implementations to understand and keep consistent.

Baseline

Measured on 2026-09-03 from JavaScript, Jade, CSS and MJS files below client/, server/, imports/ and models/:

SourceFilesLines
JavaScript and MJS798150,724
CSS7328,941
Jade11113,287
Total982192,952

Generated bundles, dependencies, translation JSON and tests are not included. The baseline is a navigation aid rather than a target to game: moving code to generated files or compressing formatting does not count as a reduction.

The largest promising areas found in the initial survey are:

AreaCurrent evidenceOpportunity
Board themesboardColors.css is 6,339 linesStore theme values once and share structural rules
Language registrylanguages.js is 1,724 linesGenerate repetitive registry entries from compact metadata
Large UI controllersSeveral files have 1,400-2,900 linesExtract genuinely repeated paging, forms and actions
AuthorizationRules occur in models, methods, publications and REST routesUse one policy function per operation
ImportersCreator modules repeat entity creation pipelinesNormalize input, then persist through one pipeline
Retired pathsDynamic Blaze references hide some unused codeRemove only with runtime and test evidence

Splitting a large file, changing Jade to Svelte, minifying source, or moving logic into a dependency does not by itself satisfy this plan.

Working rules

Each phase is completed and verified before the next begins. Record its before and after measurements below. Behavioural changes need positive and negative tests; visible changes also need an applicable UI or screenshot test. Preserve special cases instead of forcing them into an abstraction that makes the code harder to understand.

Small migrations are preferred. A phase may be stopped when measurement shows that its abstraction adds more complexity than it removes.

Phase 1: board theme declarations

client/components/boards/boardColors.css repeats selectors and declarations for every named theme. Introduce shared structural rules backed by CSS custom properties, while retaining explicit overrides for gradients, image themes, Apple Glass Pastel and other exceptional designs.

Steps:

  1. Inventory the property roles used by ordinary colour themes and classify exceptional themes.
  2. Add a shared rule set and a small variable declaration for one ordinary theme.
  3. Extend the existing theme tests so they verify computed outcomes rather than requiring the old repeated selector layout.
  4. Migrate ordinary themes in small batches and run the theme, header, public board, All Boards and checkbox tests after each batch.
  5. Compare CSS size and a representative rendered board in desktop and mobile widths. Keep special-theme CSS explicit where that is clearer.

Acceptance criteria:

  • all existing visual behaviours and theme choices remain available;
  • theme tests and relevant Playwright tests pass;
  • boardColors.css has materially fewer declarations and bytes;
  • a new ordinary solid-colour theme can be added mainly by declaring values.

Result:

  • ten ordinary solid-colour themes now declare twelve palette values and use one shared structural rule set;
  • gradient, image, Relax, Dark, Apple Glass Pastel, Modern and Clean themes remain explicit because their structure or behaviour differs;
  • boardColors.css decreased from 6,339 lines / 196,012 bytes / 1,266 rules / 2,216 declarations to 5,895 lines / 178,134 bytes / 1,058 rules / 2,064 declarations;
  • PostCSS parsed the resulting stylesheet successfully;
  • themeAccents, boardTileTheme, allBoardsPage, publicBoardsPage, headerBars, checkboxesAreSquare and appleGlassPastelTheme passed: 110 assertions in total;
  • a Chromium computed-style check was attempted at 1,280 px and 390 px, but this ARM64 sandbox only has an x86-64 Chromium binary and its emulated launch did not complete. Runtime visual verification therefore remains an environment boundary rather than a claimed result.

Status: completed in commit cd1039230.

Phase 2: language metadata

Replace repetitive hand-written language registry objects with compact metadata and generated lazy imports. The public registry shape and language order must remain unchanged. Adding a language must still require all three integration points specified by the translation policy.

Acceptance criteria:

  • every current locale is registered with the same code, tag, native name and RTL value;
  • every translation remains lazily loaded;
  • translation, key-order, placeholder and language-picker tests pass;
  • the authoritative metadata is shorter and has a duplicate-tag check.

Result:

  • the 245 language records now use one compact metadata row each;
  • a separate loader map retains one literal dynamic import() per language, preserving Meteor's per-language split points;
  • a runtime parity comparison against the previous module confirmed identical keys, codes, tags, native names, RTL flags and loader paths for all 245 entries;
  • languages.js decreased from 1,724 lines / 34,873 bytes to 510 lines / 24,362 bytes;
  • guards now reject duplicate metadata keys, duplicate language tags, duplicate loader keys, missing loaders, eager imports and files not claimed by the registry;
  • i18nLazyLoading, i18nLazyLoaded, newLanguageWiring, rtl and changeLanguageColumns passed: 31 assertions in total.

Status: completed in commit a3bc8155d.

Phase 3: repeated UI mechanics

Measure duplication in large UI files before extracting anything. Concentrate on repeated pagination, searches, menu data, modal lifecycle, form value collection and Meteor method result handling. Do not create generic helpers for code that merely looks similar but has different behaviour.

Acceptance criteria:

  • at least two real consumers use every extracted abstraction;
  • user-visible error, keyboard and accessibility behaviour is preserved;
  • focused unit tests cover the shared primitive and UI tests cover consumers;
  • total maintained code is lower after including the new abstraction.

Result:

  • one adjacentPage primitive now clamps page movement for All Boards, the generic Admin Panel reports, event streams, office reports and all four People paging contexts;
  • People maps the active pane to its page, total and page-size state once, replacing separate previous/next branches for organizations, teams, people and the login-location drill-down;
  • focused tests cover ordinary movement, both boundaries, normalized direction and invalid movement; the existing consumer suites passed with 96 assertions (tablePage 55, allBoardsPage 27, subscription lifetime 3 and grouped offices 11);
  • the 982-file maintained-source measurement decreased from 191,284 to 191,279 lines. The small five-line net reduction includes the new shared primitive; tests are excluded from that measurement and grew to pin its behaviour.

Status: completed in commit eb0e34786.

Phase 4: authorization policies

Inventory equivalent permission decisions across collection methods, Meteor methods, publications and REST endpoints. Move each equivalent decision into a pure shared policy and retain transport-specific error formatting at the edge.

Acceptance criteria:

  • DDP and REST make the same decision for the same actor and resource;
  • policies have allowed and denied tests, including missing resources;
  • publications do not expose records rejected by their matching mutation policy;
  • duplicated permission conditions are removed rather than wrapped.

Result:

  • the existing write-capability policy was confirmed to serve collection permissions, Meteor methods, REST mutations and destination checks in publications; it was retained instead of creating a competing abstraction;
  • a new transport-neutral canReadBoard policy now serves two DDP publications, two HTTP attachment routes and fourteen position-history method checks;
  • public boards allow anonymous and authenticated reads, private boards require an active member, and missing or malformed boards fail closed;
  • HTTP and DDP keep their own response/error formatting at their edges. The legacy attachment publication now makes the same public-board decision as the HTTP download routes instead of rejecting every anonymous subscriber;
  • six focused policy and wiring tests passed, together with the existing file safety, request authentication, card scope and window-publication suites;
  • maintained source decreased from 191,279 to 191,248 lines, including the new policy module.

Status: completed in commit e8c867b33.

Phase 5: importer pipeline

Define a small internal board representation for importers. Source-specific modules parse and normalize their data; one tested writer creates users, boards, swimlanes, lists, cards, comments, checklists and attachments.

Acceptance criteria:

  • existing supported import formats retain their behaviour;
  • source-specific quirks remain visible in their adapters;
  • malformed and partial imports have negative tests;
  • entity creation and ID-mapping logic has one implementation where possible.

Result:

  • WeKan JSON and Trello adapters now describe their ordered stages to one runImportPipeline implementation, which carries the created board ID and normalizes absent optional collections to empty arrays;
  • one writeImportedEntity implementation now owns direct insertion, optional timestamp touching and old-to-new ID recording for lists, swimlanes, checklists and WeKan custom fields;
  • source-specific normalization remains in the adapters: notably WeKan's real swimlanes and extra dependency/rule stages, and Trello's synthetic default swimlane and inline checklist items;
  • malformed top-level input and a pipeline that creates no board fail closed; missing optional arrays, stage order, ID propagation, mapping and timestamp writes have focused positive and negative tests;
  • six pipeline tests and 95 existing importer assertions passed. The three changed modules also passed Node 24 syntax checks;
  • maintained source decreased from 191,248 to 191,220 lines, including the new pipeline module. Obsolete commented-out importer implementations and the unused empty WeKan checker were removed as part of the same inventory.

Status: completed in commit 8b7115335.

Phase 6: evidence-based removal

Run static analysis as a candidate generator, then verify candidates against Blaze template names, dynamic imports, global registrations, server startup side effects and packaging entry points. Remove retired compatibility paths and unused feature code only after that inspection.

Acceptance criteria:

  • every removal has search evidence and relevant regression coverage;
  • application startup, production build and registered test suites succeed;
  • no supported deployment mode or documented feature is silently removed.

Result:

  • static search found two tracked model implementations whose .disabled suffix prevents Meteor from treating them as JavaScript. No import, template, startup registration, package entry or application source referenced their filenames or AttachmentsOld / AvatarsOld symbols;
  • models/attachments_old.js.disabled and models/avatars_old.js.disabled were removed: 148 tracked lines. These files are recoverable from git history;
  • supported legacy CollectionFS reads remain in attachmentBackwardCompatibility.js, the legacy attachment publication and its HTTP route. Migration extraction and file-safety suites passed;
  • a regression test verifies that the retired files and symbols stay absent while the active legacy-read modules remain present;
  • all 709 registered Node test suites passed with zero failures in 48 seconds;
  • meteor build .build-lesscode --directory completed successfully. Its only diagnostics were existing asset-size and optional MongoDB dependency warnings;
  • a development startup with a temporary WRITABLE_PATH reached Started your app and served at http://127.0.0.1:3999, after which it was stopped cleanly;
  • the maintained-source metric is unchanged at 191,220 because .disabled files were deliberately excluded from that baseline. The removal nevertheless deletes 148 tracked, reviewable lines rather than moving or compressing them.

Status: completed in commit e37717d07.

Results

PhaseBeforeAfterTestsResult
Baseline192,952 lines191,220 linesNot applicable1,732 fewer maintained lines
1. Board themes6,339 lines / 196,012 bytes5,895 lines / 178,134 bytes110 assertions passed; browser unavailableCompleted (cd1039230)
2. Language metadata1,724 lines / 34,873 bytes510 lines / 24,362 bytesRegistry parity and 31 assertions passedCompleted (a3bc8155d)
3. UI mechanics191,284 maintained lines191,279 maintained linesPrimitive and 96 consumer assertions passedCompleted (eb0e34786)
4. Authorization191,279 maintained lines191,248 maintained lines6 policy tests plus transport regressions passedCompleted (e8c867b33)
5. Importers191,248 maintained lines191,220 maintained lines6 pipeline and 95 importer assertions passedCompleted (8b7115335)
6. Removal148 tracked disabled lines0 tracked disabled lines709 Node suites, build and startup passedCompleted (e37717d07)

Final outcome

All six phases are complete. The maintained JavaScript/MJS, Jade and CSS metric decreased from 192,952 to 191,220 lines: 1,732 fewer maintained lines. Phase 6 additionally removed 148 tracked disabled lines that were outside the baseline, for 1,880 deleted or avoided lines across the measured work.

The reductions came from fewer independent implementations, not from minification or changing template technology. Replacing all Jade/Blaze views with Svelte would temporarily increase code because both UI systems, adapters and migration tests would coexist; it is therefore not a code-reduction step by itself. Further work should repeat this measured approach on one duplicated behavior at a time and retain a change only when its tests and maintenance surface improve.