AI Technical Debt Analysis
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 1k
- Forks
- 348
- Avg merge
- 12h 28m
- Merged PRs (30d)
- 91
Description
Summary
A technical analysis of the PHP code base (lib/, ~503 files excluding lib/Vendor/) identified the remaining technical debt now that the app requires PHP 8.1+ and Nextcloud 32+. The good news first: the classic legacy patterns are already gone — no \OC::$server, no OCP\ILogger, no deprecated query builder ->execute(), no legacy hooks, events go through IEventDispatcher. The remaining debt falls into three buckets: PHP 7.0-era code style at scale, two generations of the same concept coexisting, and inconsistent conventions.
This is a tracking ticket; each item below is a candidate for its own focused PR.
Deprecated API usage
- Controller annotations → attributes: 29 of 36 controllers still use deprecated docblock annotations (
@NoAdminRequired,@NoCSRFRequired, …), only 8 use PHP attributes (#[NoAdminRequired]). Mechanical migration, should be one PR. -
IConfigapp values →IAppConfig: 49getAppValue/setAppValuecall sites vs. 14IAppConfigusages — both APIs in active use for the same purpose. Standardize onIAppConfigfor app-level values.
PHP 7.0-era style (vs. PHP 8.1 floor)
- Constructor property promotion: ~355 files with constructors, only ~10 use promoted properties. Rector-assisted sweep; land before other refactors to avoid rebase pain. https://github.com/nextcloud/mail/pull/13124
- Native property types: hundreds of docblock-only
/** @var */properties, concentrated inlib/Db/entities (all 44 use untypedprotected+@methoddocblocks) and older services. Prerequisite for a stricter Psalm level (currentlyerrorLevel="3"). - Native enums: zero
enumdeclarations. Candidates:LocalMessagetype/status constants,RecipientTYPE_*,Addresstype constants,OutOfOfficeParser/FilterParserstate constants,Model\Classifier. Start with pure in-memory ones; DB-backed need backed enums + mapper care. - Leftover string idioms: 4 remaining
strpos() !== falsepatterns →str_contains(); a handful ofswitchstatements that would read better asmatch. https://github.com/nextcloud/mail/pull/13125
Same thing, different concepts — alignment needed
- Two JSON response classes: custom
OCA\Mail\Http\JsonResponse(19 controllers) vs. OCPJSONResponse(15 controllers); inconsistent status code conventions. Converge on the custom one. -
#[TrapError]coverage: applied in 26 of 36 controllers; the rest hand-roll try/catch or leak 500s. Apply uniformly and standardize services on throwing typedOCA\Mail\Exception\*exceptions instead of returning sentinel nulls. - Two sending pipelines:
lib/Send/handler chain vs. the olderMailTransmissionmonolith with overlapping responsibilities (sent-folder copy, flagging). Decide the chain is the target and shrinkMailTransmissionto SMTP transport. - Sync layer ownership:
MailboxSync→SyncService→ImapToDbSynchronizer→IMAP\Sync\Synchronizer— responsibilities leak between the middle two (both know about locking and mailbox state). Should be written down/cleaned before further sync work lands. -
MessageMappername collision:Db\MessageMappervs.IMAP\MessageMapperforces import aliases in ~10 files with per-file alias spellings. Rename the IMAP one (e.g.ImapMessageFetcher). Same forFolderMapper. - Serialization: 43 ×
jsonSerialize(), one bespoketoJson()(Db\MailAccount), plus hand-built arrays in some controllers. Converge onJsonSerializable. - AI APIs span two generations: TextProcessing (sync: smart replies, follow-up, translation, event extraction) vs. TaskProcessing (async: summaries);
TaskProcessingListenerdistinguishes task origins by sniffing for'max_tokens'in the prompt. Migrate to TaskProcessing behind one internal abstraction.
Smaller items
- Config keys as scattered string literals (
'allow_new_mail_accounts','layout_message_view','llm_processing', …) — introduce a constants class. - IMAP client lifecycle: ~60% of call sites use
try/finally { logout() }; introduce awithClient(callable)wrapper to make the safe pattern the only pattern. - N+1 in
SnoozeService(per-messagegetSrcMailboxId()lookup). -
MessageMapper::updateBulk()binds boolean flags withPARAM_INTinstead ofPARAM_BOOL. - 2–3 direct
new DateTime('now')calls bypassingITimeFactory(ProxyDownloadResponse,FollowUpClassifierListener). - Provisioning checks (
getProvisioningId() !== null) repeated in 4+ places — extractAccount::isProvisioned(). - Move the two global functions in
lib/functions.php(array_flat_map,chunk_uid_sequence) into a utility class. - Only
MessagesControllerpaginates; other list endpoints return unbounded result sets.
Suggested order
- Controller attributes migration (deprecation with a server-side deadline)
- One JSON response class +
#[TrapError]everywhere - Constructor property promotion sweep (before any typing work)
IAppConfigmigration + config key constants- Native property types → stricter Psalm level
- Enums, naming collisions, lifecycle wrapper, remaining items
This analysis was produced with AI assistance (Claude Code, claude-fable-5); headline counts were verified manually with grep. Full report: TECHNICAL_DEBT_ANALYSIS.md (local).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lib/ and the local TECHNICAL_DEBT_ANALYSIS.md report, then select one checkbox rather than treating this tracking issue as a single task. Use the suggested order to choose a focused area; done means a separate, scoped PR addresses that item and verifies its stated migration or cleanup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100