← All articles

10 iOS Code Commenting Best Practices for 2026

A deep dive into code commenting best practices for Swift/iOS. Learn modern rules for documentation, AI agents, and shipping apps that pass review.

10 iOS Code Commenting Best Practices for 2026

Comments are part of the build system now.

Self-documenting code still matters, but it does not cover the problems modern iOS teams hit. Swift concurrency can hide ordering assumptions. RevenueCat integrations carry business rules that do not belong in type names. Firebase setup often depends on environment-specific behavior. AI assistants like Cursor and Claude can produce tidy code that subtly breaks those constraints unless the repository gives them explicit guidance.

That raises the bar for code commenting best practices. Good comments preserve intent, mark architectural boundaries, explain why a workaround exists, and warn both humans and tools about code that looks simpler than it is. The failure mode is familiar on small app teams. Someone cleans up a guard, renames a service, or removes a "temporary" branch, then subscriptions stop syncing or review builds start reading production config.

Comment count is a useless proxy for that problem. A codebase can be full of comments and still communicate nothing if those comments restate the obvious.

Useful comments do real work. They help a developer decide what can change safely. They help an AI agent avoid rewriting a fragile integration with App Store, RevenueCat, or backend assumptions that are only visible in production. For indie iOS teams, that is the standard that matters.

Table of Contents

1. Comment Why, Not What

Readable code does not need a narrator. It needs context.

The weakest comments just restate the line below them. // fetch subscription status above a RevenueCat call helps no one, including AI tools reading the file for context before they suggest an edit. Good comments explain the reason a piece of code exists, what risk it is containing, or which product rule it is protecting.

That distinction matters more on iOS than many teams admit. A branch that looks awkward in Swift often exists because of App Review behavior, StoreKit edge cases, state restoration bugs, or a conversion decision someone tested and chose to keep. If that reason is not written down, the next cleanup pass removes it, and the app gets worse while the code looks better.

Practical rule: Write the comment for the invariant, not the implementation. If the code changes and the reason still holds, the comment earned its place.

A few examples from real app code:

  • Weak comment: // Get user subscription status

  • Better comment: // Gate offline export on active premium entitlement. Fresh installs can read stale local state before RevenueCat finishes syncing.

  • Weak comment: // Send analytics event

  • Better comment: // Track paywall dismissal separately from close tap. We use this to measure drop-off after impression and before purchase attempt.

  • Weak comment: // Use Task here

  • Better comment: // Run off the main actor because this purchase refresh can block UI updates long enough to make the paywall feel broken.

Comments like these help humans. They also give AI assistants better instructions. Cursor or Claude can usually infer what a function does from Swift types and naming. They are far less reliable at inferring why a weird branch exists, which side effect must stay, or what business rule should survive a refactor. If you want useful AI output, leave machine-readable intent in the codebase, not just clean syntax. The broader Spaceport engineering blog follows the same pattern. Document the decision pressure, not only the code path.

Business context is where comment quality usually breaks down. Six months later, nobody remembers why the paywall appears on session two instead of first launch, why trial users skip a step in onboarding, or why a SwiftUI screen targets iOS 17 instead of carrying a fallback. Those decisions look arbitrary after the experiment, incident, or App Review rejection is gone. A short comment preserves the reason.

When comments are worth writing, they usually capture one of three things:

  • Constraint: Apple platform behavior, SDK limitations, or review requirements forced this shape.
  • Trade-off: The code is less elegant, but it protects revenue, data integrity, or support load.
  • Intent: The branch exists to preserve conversion, retention, privacy, or recovery behavior.

That is the level where comments start paying rent.

2. Implement ADRs and Provide ArchitectureModule Overviews

Clean code does not explain system boundaries. In an iOS app with SwiftUI, StoreKit, RevenueCat, background sync, and a growing pile of AI-assisted edits, that missing layer is where teams get hurt.

Architecture Decision Records and module overviews fix a specific problem. They capture the rules that are bigger than any one file: who owns entitlement state, where onboarding is allowed to trigger purchases, why a feature lives in its own package, and which shortcuts are banned because they caused bugs before.

A laptop showing a chart next to a printed Architecture Decision Record document and a glass of water.

For a RevenueCat integration, I want a short record near the code that answers three things fast. Why the team chose RevenueCat instead of hand-rolling StoreKit flows. Which type or module is the source of truth for customer info and entitlements. Which callers are allowed to mutate purchase state. Without that, a new engineer or an AI assistant will happily add purchase checks in random view models and create state drift.

The same rule applies to feature modules. An onboarding folder should open with a clear overview of screen order, auth handoff, analytics events, side effects on completion, and any experiment logic that must stay coordinated. That is not documentation theater. It prevents duplicate logic, broken funnels, and "small refactors" that unintentionally change conversion behavior.

Architecture comments should answer system questions

A useful module overview answers the questions reviewers ask after they already feel uneasy:

  • What does this module own?
  • Who is allowed to call into it?
  • Which dependencies does it touch?
  • What state changes leave this module?
  • What logic must not be reimplemented elsewhere?

That structure matters even more for indie teams shipping fast with templates, code generation, and AI tooling. Generated code is usually plausible. Plausible is not the same as aligned. The Spaceport engineering blog is a good model for this kind of system-level clarity. Keep the same standard inside the repo.

Keep ADRs near the code they govern

ADRs in Notion go stale because they are outside the review path. ADRs in the repo get read because they sit next to the files people edit.

Use a short top-of-file block or a module README with a fixed format: context, alternatives, decision, consequences. Keep it short enough to survive code review. If it takes five minutes to scan, nobody will use it. If it takes thirty seconds, it becomes part of how the team works.

A simple rule helps here:

Accepted decisions should be visible where new code gets written, not buried where no one looks.

Treat these comments as machine-readable instructions too. If your repo uses Cursor or Claude, point those tools at the same architecture notes through .cursorrules, CLAUDE.md, or repository instructions. That gives generated code a chance to follow module boundaries, dependency rules, and ownership conventions instead of producing locally correct code that damages the app's overall shape.

A short walkthrough helps when you're setting this up for the first time:

3. Use Type-Safe Comment Markers TODO FIXME HACK WARN

Loose comment markers create fake clarity. A repo full of random TODO, FIXME, and HACK tags trains people to ignore all of them, including the ones that matter. The fix is simple: give each marker one meaning, one format, and one review standard.

A modern computer monitor displays code with highlighted markers on a clean wooden office desk.

For modern iOS teams, these markers do more than help humans scan a file. They give Xcode, SwiftLint, grep, and AI assistants a consistent signal about debt, risk, and intent. If Cursor or Claude reads // HACK(paywalls):, that is far more useful than // weird fix because the tool can infer ownership, urgency, and likely cleanup work.

Use each marker for one job only:

  • TODO for planned work that has not been done yet
  • FIXME for behavior that is wrong today
  • HACK for a temporary workaround you expect to remove
  • WARN for code that can break purchases, data integrity, compliance, or app review behavior if changed casually

The formatting matters as much as the label. // TODO: fix later is noise. // FIXME(growth): RevenueCat entitlement refresh can race paywall dismissal. Ensure caller awaits refresh before reading premium state gives a teammate, or an AI coding agent, enough context to act without guessing.

I prefer a small, enforced schema:

  • TODO(owner): task and the condition that closes it
  • FIXME(owner): current bug and expected behavior
  • HACK(owner): why the workaround exists and when to delete it
  • WARN: what breaks, and under what condition

That last part is where teams usually get lazy. They write the problem and skip the trigger. Cleanup comments without a removal condition survive for years.

Examples from real iOS work:

  • TODO(mobile): replace sandbox RevenueCat API key before TestFlight production submission
  • FIXME(auth): Apple relay email may arrive before verified profile sync. Preserve existing confirmed email until server reconciliation completes
  • HACK(paywalls): keep animation reset workaround for early iOS 17 builds. Remove after minimum supported OS excludes affected versions
  • WARN: changing product identifier mapping can orphan introductory offer logic and skew conversion tracking

This also pays off outside engineering. Product work tied to pricing tests, paywall experiments, and release timing tends to move fast. Clear markers help a small team ship those changes without losing track of temporary decisions. That matters when you're iterating on conversion work informed by practical App Store optimization tactics for indie iOS apps.

A good rule is easy to enforce in review: if a marker cannot be searched, assigned, or linted, it should not be in the codebase. Comments are not decoration here. They are lightweight instructions for humans and machines.

4. Document Public APIs and Entry Points Comprehensively

Public APIs are where small ambiguities turn into shipped bugs. Inside a module, engineers can read surrounding code and infer intent. At an entry point, callers should not have to guess. That applies to teammates, future-you, and AI tools reading your codebase through docs, symbols, and nearby comments.

On iOS, the expensive mistakes usually happen around async boundaries and state transitions. A Swift signature shows types. It does not show whether the method hits RevenueCat or cache, whether cancellation is safe, whether it must run on the MainActor, or whether a failure should trigger UI, retry in the background, or be ignored during launch.

Write API docs from the caller's point of view. I want the comment to answer the questions someone has before they invoke the method, not after they read the implementation.

Useful API docs usually cover five things:

  • what the API guarantees
  • what can fail, and how that failure is represented
  • side effects such as analytics, persistence, purchases, or network calls
  • state changes the caller should expect
  • the next step the caller is responsible for

Use Swift doc comments on public types, protocols, functions, and real app entry points such as purchase coordinators, auth managers, feature flag clients, analytics facades, and background sync triggers. If you expose a paywall presenter, document whether dismissal, cancel, restore, and purchase success are distinct outcomes. If you expose an entitlement refresh method, document cache policy, network behavior, threading, and whether the result is safe to use for immediate UI decisions.

This matters even more for teams shipping fast monetization experiments. Pricing tests, onboarding variants, and paywall changes often ride through these interfaces. Teams doing that kind of iteration, especially those applying practical App Store optimization tips for indie iOS apps, need public APIs that explain behavior clearly enough to change flows without reverse-engineering every dependency.

A good public API comment also works as machine-readable instruction. Cursor, Claude, and Xcode tooling all do better when the docstring names invariants, side effects, and expected call order in plain language. “Must be called after configure()” is more useful than “initializes purchase state.” “Returns cached entitlements and schedules a refresh” is more useful than “fetches customer info.”

A smartphone screen displaying API documentation for the Brin application next to a notebook and pens.

For complex modules, aim for full doc coverage on public surfaces. That does not mean writing essays. It means every callable boundary explains contract, failure mode, and side effects well enough that another engineer can use it correctly without opening three more files. Public APIs spread assumptions fast. Comments at those seams should stop bad assumptions before they enter the app.

5. Add Context Comments for Non-Standard or Fragile Code

Clean code can still break your app.

The code that deserves the clearest comments is usually the code a well-meaning engineer will try to simplify. In iOS work, that often means logic shaped by SwiftUI lifecycle quirks, StoreKit timing, RevenueCat sync behavior, SDK bugs, or OS-specific regressions. If the code looks unusual for a reason, write the reason down where the change would happen.

Context comments should answer three things fast. Why this implementation exists. What fails if someone rewrites it. Which dependency or condition forced the trade-off. That turns a comment from a note into an instruction, and modern AI tools handle those instructions surprisingly well. Cursor or Claude will often preserve a constraint if the comment states it plainly. If the constraint lives only in one engineer's memory, the next refactor will erase it.

Warn maintainers before they "clean up" the wrong thing

Good candidates for context comments in iOS projects include:

  • ATT and ad-related sequencing that must happen in a specific order
  • SwiftUI ownership patterns kept to avoid state resets or duplicate side effects
  • async ordering requirements in analytics or paywall event pipelines
  • retry logic around subscription refresh and entitlement reconciliation
  • temporary workarounds for specific iOS or SDK versions

Write these comments like guardrails, not diary entries. "Keep this on MainActor because entitlement changes trigger visible UI updates" is useful. "Do not switch this to @StateObject. Reinitialization here duplicates purchase observers and can desync paywall state" is useful. "weird workaround" is useless.

I have found that the best fragile-code comments read almost like failing test names. They name the condition and the consequence. That is what helps another engineer, or an AI assistant generating an edit, decide whether a cleanup is safe.

Don't comment that code is odd. Comment which failure it prevents.

Avoid restating the implementation. Fragile-code comments earn their place by preserving context the code cannot express on its own. A loop, delay, actor hop, or extra cache layer may look unnecessary in isolation. The comment should make the hidden constraint visible.

When possible, add a concrete pointer to the source of the constraint. That might be an Apple Feedback ID, a GitHub issue number, a RevenueCat support thread, an internal bug ticket, or a reproducible test name. Even without a link, naming the dependency and failure mode helps. "RevenueCat restore path can briefly return stale customer info during app foreground transition" gives a future maintainer something actionable. "purchase bug" does not.

6. Use Inline Comments Sparingly and Strategically

Inline comments are often a code smell. In Swift, they usually show up where naming, extraction, or better types should have done the job first.

Use them anyway, but make them pass a higher bar.

Inside a function, an inline comment should explain a decision that the code cannot carry on its own. Good candidates include execution order that looks arbitrary, business rules with product or revenue consequences, and branches that an engineer or AI assistant will "simplify" into a bug. On small iOS teams, that last case matters more than people admit. Cursor or Claude will happily remove a line that looks redundant if you never told it what breaks.

Bad:

if entitlementExpireDate < Date() {
    // Check if expiration date is before now
    showPaywall()
}

Better:

if entitlementExpireDate < Date() {
    // Expired subscribers go to paywall instead of restore flow, because lapsed access is a conversion path, not an auth failure.
    showPaywall()
}

The difference is intent. The first comment parrots the condition. The second preserves product reasoning. That is useful in code review, six months later during a refactor, and when an AI tool proposes a cleanup.

A simple filter works well here. If a stronger name, a small helper, or a better enum can replace the comment, change the code. If the hidden constraint still matters after that refactor, keep the comment and make it specific.

The best inline comments read like constraints:

  • Ordering constraints: a call must happen before another call, and reordering causes a visible bug
  • Policy decisions: the branch reflects pricing, attribution, App Store, or entitlement behavior rather than pure control flow
  • Intentional asymmetry: similar cases are handled differently for a reason
  • AI guardrails: code that looks redundant to a machine but protects a real workflow

That last category is new, and it changes the standard. Comments are no longer only for the next engineer. They are also instructions for tooling. A short note like // Keep as two steps. RevenueCat state may update between fetch and UI render, and collapsing this can show stale entitlement text gives both humans and agents a reason to leave the sequence alone.

Use inline comments to mark landmines, not to narrate every step. Too many comments turn a function into a transcript. A few precise ones can stop the wrong refactor.

7. Maintain a Living CHANGELOG and Deprecation Comments

Deprecation without a migration path is how dead code stays alive for another year.

Indie iOS teams feel this fast. One person marks an API deprecated, another keeps copying an old call site from a view model, and an AI assistant happily suggests the obsolete version because the repo still contains ten examples of it. The warning exists, but the instruction is incomplete.

Good deprecation comments do more than announce decay. They redirect usage.

A useful deprecation note answers three questions:

  • What should replace this API
  • What the caller has to change
  • When removal is expected

In Swift, put that guidance where the compiler can surface it with @available(*, deprecated, message: "..."). Then mirror it in a living changelog that records the repo-wide decision, the reason, and the cleanup window. If Analytics.log() is being replaced with a typed event layer, say that in the deprecation message. If a custom RevenueCat adapter is going away because the SDK now covers the same path, record the migration impact in the changelog so nobody has to reverse-engineer intent from git history.

This matters for tools as much as humans. AI coding assistants pattern-match against what they see in the codebase. If old APIs are marked weakly, and the changelog never spells out the replacement, agents will keep generating stale code. Clear deprecation comments act like machine-readable policy. They tell Cursor or Claude which examples are legacy and which path is approved.

The changelog should stay close to engineering reality. Skip marketing-style release notes. Write entries that help during refactors:

  • deprecated symbol or module
  • replacement API or pattern
  • breaking behavior change
  • expected removal version or date
  • migration example for the call site engineers are likely to copy

One pattern works well in practice. Mark the symbol deprecated in code, add a short migration example in the message or nearby docs, then add a changelog entry with the broader rollout plan. That three-part trail stops new usage, helps reviewers reject regressions, and gives AI tools a consistent signal about which code is current.

8. Document Edge Cases, Errors, and Recovery Strategies

Happy-path comments are cheap. Failure-policy comments are the ones that prevent support tickets, duplicate purchases, and broken state after app resume.

This section is where many iOS codebases get vague. The code handles an error, but the comment never explains the product decision behind that branch. That gap hurts twice. Reviewers cannot tell whether the fallback is intentional, and AI assistants start treating incidental behavior as policy.

Comment the recovery contract

For subscription apps, comments should spell out what the app does under stress and why. If a RevenueCat customer info refresh fails, say whether the app keeps cached entitlements temporarily, forces a retry, or blocks access until verification completes. If analytics submission fails, say whether events are queued, dropped, or retried only on Wi-Fi. If Sign in with Apple returns partial identity data, say which field becomes the canonical user identifier and which values are considered disposable.

The useful part is the user-visible outcome. "Retry on error" is weak. "Keep premium active from the last known entitlement snapshot for one session, then require refresh" gives engineers and tools an actual rule to follow.

Comments worth keeping around:

  • show cached access state while entitlement refresh is in flight
  • keep premium enabled during billing grace periods unless RevenueCat explicitly reports expiration
  • retry non-critical telemetry in the background, but surface failures that block checkout or restore
  • suppress duplicate paywall presentation after foreground transitions or interrupted purchase flows
  • treat local receipt validation disagreement as a recoverable sync issue, not immediate access revocation

Recovery comments also need to name the trigger and the stopping point. A retry loop without limits becomes battery drain and noisy logs. A silent fallback without an expiry becomes permanent stale state. Good comments document both. For example, if a paywall request fails, say whether the app falls back to a bundled offering, how long that fallback is trusted, and what event should force a fresh fetch.

For indie teams, these comments carry product, support, and QA context in one place. They also help AI coding tools generate safer edits. Cursor or Claude can follow a comment like "do not revoke access on transient backend mismatch, wait for next foreground sync" far more reliably than they can infer intent from nested if statements.

One more practical point. Some edge-case comments belong beside security-sensitive recovery code, especially around auth, receipts, and account linking. The patterns in these iOS app security best practices are worth referencing directly when a "temporary fallback" could accidentally weaken verification or expose the wrong account state.

Write comments for the branch that will wake you up at 2 a.m., not the one that already reads cleanly.

9. Document Configuration, Secrets, and Environment-Specific Code

A surprising amount of "application logic" failure is really configuration failure wearing a disguise. Wrong RevenueCat key. Staging Firebase pointed at production. Feature flag missing in one scheme. Privacy-related plist value set differently across targets. The app compiles, the screen renders, and then behavior goes sideways.

Here, comments should be blunt.

Most silent failures start in configuration

Configuration comments should explain what value belongs where, what environment it applies to, and what breaks when it's wrong. They should also point people toward the sanctioned source of truth, whether that's Build Settings, .xcconfig, Info.plist entries, or CI-managed secrets.

A few examples that deserve permanent comments:

  • sandbox versus production purchase configuration
  • local emulator usage for Firebase
  • conditional compilation around debug telemetry
  • fail-fast behavior when required secrets are absent
  • environment-specific auth callback URLs

Security guidance belongs here too. Teams moving fast with templates and generators still need explicit warnings around secrets handling, release configuration, and production data exposure. The practical patterns in these iOS app security best practices are worth encoding directly in comments wherever setup mistakes can hurt users or revenue.

One contrarian point: some configuration comments should instruct the app to crash loudly in development. Silent fallback sounds friendly, but it often hides bad setup until launch week. A comment like "intentionally fails startup when RevenueCat key is missing to prevent false-positive purchase testing" prevents someone from "improving" that guard away.

If a misconfiguration can pass code review unnoticed, it needs stronger comments than ordinary app logic.

10. Use Comments to Guide AI Agents and Enable Automated Tooling

Good comments now have a second job. They guide humans during review, and they constrain tools that generate, refactor, and move code.

On an iOS team using Swift, Cursor, Claude, or Codex, that matters immediately. The common failure mode is not syntax. It is plausible code that breaks your boundaries. An assistant drops RevenueCat access into a View, recreates networking inside a feature module, or leaves behind a cheerful TODO with no owner, no scope, and no exit condition.

Treat comments as local policy. Treat rule files as global policy.

The durable rules belong in .cursorrules, CLAUDE.md, or whatever your team uses as the project contract. The nearby comments should restate the constraints that are easy for both humans and agents to violate in that file or type. I have found that repetition is cheaper than cleanup when an AI tool "helpfully" spreads a shortcut across six call sites.

Use markers that are boring and explicit:

  • RULE: architectural constraint or required invariant
  • ENTRY-POINT: approved seam for external access
  • WARN: dangerous dependency, side effect, or forbidden shortcut
  • EXAMPLE: accepted and rejected usage
  • AI-NOTE: instruction for generated edits, only when a normal comment is too ambiguous

A purchase flow is a good place to be strict. A local comment can say that PurchaseManager is the only type allowed to call RevenueCat, entitlement state must be observed through a specific interface, and paywall presentation stays in SwiftUI coordination code. That gives a reviewer something concrete to check. It also gives an agent fewer excuses to improvise.

// RULE: Only PurchaseManager may call RevenueCat SDK types directly.
// WARN: Do not import RevenueCat into Views, ViewModels, or feature reducers.
// ENTRY-POINT: Use PurchaseServicing for purchases and entitlement refresh.
// EXAMPLE: Allowed -> PurchaseManager.purchase(package)
// EXAMPLE: Rejected -> PaywallView calling Purchases.shared.purchase(...)

This style works because the instruction is testable. "Keep this clean" is useless. "Do not import RevenueCat outside the purchase layer" can be checked by a reviewer, a linter, or a simple script in CI.

The old advice on comments still applies, but AI-assisted codebases need one extra standard. Comments should be written so a machine can follow them consistently. The Stack Overflow blog discussion focused on writing comments that age well and stay useful. In practice, that same discipline makes comments better inputs for automation.

One warning from experience. Do not turn comments into a second spec that nobody maintains. Only annotate the rules that prevent expensive mistakes: billing boundaries, threading assumptions, storage ownership, analytics contracts, and codegen-safe extension points. If a comment cannot influence a review, a script, or an AI edit, it is probably noise.

Write comments so a linter, an AI assistant, and a tired human reviewer all make the same call.

That is the standard that holds up in a modern Swift codebase.

Top 10 Code Commenting Best Practices Comparison

Practice 🔄 Complexity ⚡ Resource requirements 📊 Expected outcomes Ideal use cases 💡 Key tips
Comment Why, Not What Medium, requires domain knowledge Moderate time and expert reviewers Clear design intent; faster onboarding and reviews Business rules, entitlement checks, analytics events Explain intent, reference business docs, update when logic changes
Implement ADRs & Module Overviews High, process and discipline required High, write, review, and maintain ADRs Long-term architectural consistency and fewer repeated debates System-level decisions, module boundaries, conventions Number ADRs, place in READMEs, mark status (Proposed/Accepted)
Use Type-Safe Comment Markers (TODO/FIXME) Low, simple conventions Low, easy to adopt; CI integration recommended Trackable list of known issues; CI-enforceable quality gates Tracking temporary work, build-time checks, code hygiene Standardize format, include owner/ticket, enforce via linter
Document Public APIs & Entry Points Medium–High, detailed writing High, time to document examples and errors Safer API consumption; better auto-help and generated docs Public SDKs, shared modules, generated boilerplate Use docstrings with examples, document errors and side effects
Add Context Comments for Fragile Code Medium, needs platform knowledge Moderate, research and linking external refs Fewer regressions and App Review issues; safer refactors Platform quirks, workarounds, fragile integrations Use ALL CAPS warnings, link bug reports and fix versions
Use Inline Comments Sparingly & Strategically Low, style discipline Low, small, targeted comments Cleaner code and easier maintenance; signals need to refactor Complex logic blocks that can't be named clearly Refactor first, comment groups not lines, prefer descriptive names
Maintain a Living CHANGELOG & Deprecations Medium, ongoing process Moderate, update with releases and migrations Clear upgrade paths and reduced surprises on updates Libraries, boilerplate releases, API evolution Link deprecations to changelog, use @available warnings
Document Edge Cases, Errors & Recovery Medium–High, deep product knowledge Moderate, document timeouts, retries, UX behaviors More robust behavior during outages; fewer billing/data losses Revenue-critical flows, offline behavior, retries State timeout rationale, document fallbacks and user-visible effects
Document Configuration, Secrets & Envs Medium, sensitive and detailed Moderate, verify setups for each env Fewer silent failures and setup mistakes Multi-environment projects, API keys, build configs Avoid hardcoding, document sandbox vs prod, fail fast if missing keys
Use Comments to Guide AI Agents & Tooling High, structured, machine-readable rules High, create .cursorrules, linters, agent docs Automated enforcement of architecture and better AI suggestions Projects using AI code generation or custom linters Add .cursorrules, use RULE/EXAMPLE markers, keep patterns machine-readable

From Comments to Conventions A System for Quality

The best code commenting best practices don't produce more text. They produce more clarity per line.

That's the part many teams miss. Comment quality isn't a writing problem first. It's a systems problem. If comments are inconsistent, stale, unsearchable, or disconnected from architecture, they become visual clutter. If they're deliberate, structured, and tied to real decisions, they become part of how the team ships safely.

That matters even more for indie iOS teams. In a small shop, one person is often acting as product manager, iOS engineer, release engineer, support fallback, and sometimes prompt engineer for AI tooling. You don't have the luxury of tribal knowledge living in separate people. The repo has to carry that load. Comments are one of the cheapest ways to preserve intent where it matters most.

The old advice to avoid comments unless absolutely necessary came from a good instinct. People were trying to fight noise. But taken too strictly, that advice leaves modern app teams under-documented at exactly the seams where risk lives: subscription state, async behavior, auth flows, analytics sequencing, configuration, and generated code boundaries.

Effective comments also create advantages outside the editor. They make onboarding easier. They improve reviews. They give AI assistants cleaner constraints. They help you deprecate old paths without chaos. They stop "cleanup" refactors from deleting defensive code that protects revenue or App Review compliance.

The strongest approach is to treat comments as one layer in a broader convention system:

  • architecture rules in ADRs and module headers
  • API docs on all public seams
  • searchable markers for pending work and known compromises
  • warning comments on fragile or policy-sensitive code
  • changelog entries tied to deprecations
  • rule files like .cursorrules and CLAUDE.md for machine enforcement

That combination creates a feedback loop. Engineers write clearer comments. AI tools generate code that follows those comments. Reviewers catch drift faster because intent is explicit. The codebase stays easier to extend because the reasoning behind it is still visible.

For teams working in Swift, RevenueCat, Firebase, and modern AI-assisted workflows, this isn't optional polish. It's operational discipline. Tools can generate syntax at high speed. They still need humans to encode intent. Comments are one of the clearest places to do that.

Spaceport is a useful example of this mindset in product form. It doesn't just generate SwiftUI app structure. It also ships the conventions, AI guidance files, and prewired architecture that help those projects stay maintainable after generation. That's the right direction for modern mobile engineering. Not more comments for the sake of comments. Better comments, connected to the way the whole system works.


If you want a faster starting point for production-ready SwiftUI apps, Spaceport gives indie iOS teams a generated project with the architecture, RevenueCat wiring, Firebase setup, AI rule files, and App Review-aware defaults already in place. That means you can spend less time inventing conventions from scratch and more time shipping an app that stays readable as it grows.

Built with Outrank app

Community appsJoin Discord