Skip to content

[e-navigator] Introduce Steep Typechecking#232

Merged
hayat01sh1da merged 37 commits into
masterfrom
hayat01sh1da/e-navigator/introduce-steep-typechecking
May 16, 2026
Merged

[e-navigator] Introduce Steep Typechecking#232
hayat01sh1da merged 37 commits into
masterfrom
hayat01sh1da/e-navigator/introduce-steep-typechecking

Conversation

@hayat01sh1da

@hayat01sh1da hayat01sh1da commented Apr 14, 2026

Copy link
Copy Markdown
Owner

1. Why (Purpose)

Rails applications are dynamically typed, so regressions in controllers, models, mailers, jobs and forms are only caught at runtime or by tests.
Adding rbs-inline (signatures derived from annotated source) together with Steep (the type checker) introduces static verification of method existence, arguments and return types across the codebase.

A key motivation is that an apparently small error count is usually misleading.
When the Rails framework signatures are absent, Steep degrades whole class hierarchies to untyped and silently suppresses everything that depends on them.
The objective is therefore not "make the reported errors disappear" but stand up a complete, reproducible type-checking pipeline so the check is genuinely green — framework types resolved, application code annotated, and the real type defects that surface afterwards actually fixed.

2. What (Procedures)

Introducing the pipeline follows a fixed sequence:

  1. Dependencies: add steep, rbs-inline and rbs_rails to the development/test bundle.
  2. Framework signatures: initialise and install an RBS collection (rbs_collection.yaml + lock) so framework and common-gem signatures are pulled from a shared, version-pinned source.
  3. Generated signatures: register the rbs_rails rake task to emit model and route-helper signatures, and run rbs-inline to emit signatures from annotated application and test code.
  4. Configuration: author the Steepfile with separate targets for application and test code, so permissive test-only signatures never weaken the strictly-checked application code.
  5. Hand-written shims: for libraries with no published RBS and for constructs the generators cannot express (test DSLs, authentication helpers, dynamically defined helpers, individual missing methods),add minimal hand-written signature files, kept in their own directory and loaded only by the target that needs them.
  6. Real fixes: resolve the genuine type defects that become visible once the framework is typed (nil-handling, ambiguous empty collections, unsupported syntax, etc.) by changing code where it is a real bug, or narrowing types where the upstream RBS is incomplete.
  7. Verify and record: run the check until it is green, repeat to confirm stability, and land the work as small semantic commits (dependencies → configuration → generated signatures → hand-written shims → code fixes).

Context-specific note: when the app runs only in containers, every step above is executed inside the container; when model signatures are generated for apps with a database, the database service must be up and prepared first.

3. How (Operation and Maintenance)

3-1. Run the check

Through the project's normal execution environment(locally or inside the container), invoking steep check.

3-2. Regenerate signatures

As a build step — install the RBS collection, run rbs-inline over the source, and run the rbs_rails task for models and routes. This is mandatory on a fresh checkout whenever generated signatures are treated as build output rather than committed.

3-3. Track vs. regenerate

  • Track: the Steepfile, the collection manifest and lock, the rake task registration, and all hand-written shims (force-track them if they live under an otherwise-ignored signature directory).
  • Do not track: the downloaded collection cache, regenerable generated signatures (when the project chooses to rebuild them), and the check's log output (a regenerable artifact).

3-4. Maintenance triggers

  • Upgrading the framework or gems → update and reinstall the collection, then patch shims where collection coverage lags the new version.
  • Adding or changing models or routes → re-run the rbs_rails task.
  • Adding or changing application or test code → re-run rbs-inline.
  • Using new library surface (auth, test DSL, etc.) not yet in a shim → extend the relevant hand-written shim.
  • Type-checker versions may not support the newest Ruby syntax; keep checked code within the supported subset.

4. Pros & Cons

4-2. Pros

  • Genuine end-to-end coverage (framework + application + models + routes), not merely silencing the initially reported errors.
  • Reproducible: signatures are regenerable from version-pinned, committed configuration.
  • The application/test target split keeps application code strictly checked while test-only relaxations stay quarantined.
  • Low behavioural risk: most fixes are signatures and config; code edits are minimal and semantically equivalent.
  • Catches regressions continuously once integrated into the workflow.

4-2. Cons

  • Hand-written shims are a standing maintenance surface and are deliberately permissive — they unblock the checker rather than fully model those libraries.
  • Shared signature collections lag new framework releases; version drift can reintroduce gaps after upgrades.
  • Generated signatures are a required build step, so a fresh clone is not green until regeneration runs.
  • The pipeline adds environment and setup cost (toolchain, and a database/container when those are required to generate signatures).
  • Type-checker support trails the newest Ruby syntax, constraining some modern constructs in checked code.
  • Force-tracking shims inside an otherwise-ignored directory is a deliberate exception to ignore conventions and is easy to overlook.

@hayat01sh1da hayat01sh1da self-assigned this Apr 14, 2026
@hayat01sh1da hayat01sh1da reopened this Apr 14, 2026
@hayat01sh1da hayat01sh1da changed the title [e-navigator] Introduce Rubocop for Static Code Analysis Introduce Steep Typechecking Apr 14, 2026
@hayat01sh1da hayat01sh1da changed the title Introduce Steep Typechecking [e-navigator] Introduce Steep Typechecking Apr 14, 2026
@hayat01sh1da hayat01sh1da force-pushed the hayat01sh1da/e-navigator/introduce-steep-typechecking branch from 9e92a61 to 5376c6d Compare April 23, 2026 20:36
@hayat01sh1da hayat01sh1da force-pushed the hayat01sh1da/e-navigator/introduce-steep-typechecking branch 3 times, most recently from a7c4047 to 30ced9f Compare April 23, 2026 21:08
@hayat01sh1da hayat01sh1da force-pushed the hayat01sh1da/e-navigator/introduce-steep-typechecking branch from 30ced9f to 7c89dd7 Compare April 23, 2026 21:12
hayat01sh1da and others added 17 commits April 27, 2026 00:38
…ypechecking

Co-authored-by: Copilot <copilot@github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split into app/test targets so the test-DSL shim never relaxes application code; ignore the downloaded .gem_rbs_collection vendor dir.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Devise, ActionMailer::TestCase and the Devise test helpers have no RBS in the gem collection. Force-tracked because e-navigator/sig is otherwise git-ignored as generated output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@hayat01sh1da hayat01sh1da left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hayat01sh1da hayat01sh1da merged commit 03ecf29 into master May 16, 2026
10 checks passed
@hayat01sh1da hayat01sh1da deleted the hayat01sh1da/e-navigator/introduce-steep-typechecking branch May 16, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant