skip to content

Before reshaping a column, how do you find every query that touches it under mixed authoring styles?

level: seniorimportance: should knowfreq 52%

answer

  1. only as good as the weakest style
  2. compiler beats search
  3. parse declarations in the build
  4. runtime evidence for assembled text
  5. expand, migrate, contract

basics

~20 s

Rank the evidence: regenerate typed symbols and let compilation list the call sites, parse every declared query in the build, search literal text and derived method names, then read the statements actually executed. Expand-migrate-contract keeps a miss survivable.

solid answer

~50 s

Start from the strongest evidence available. Where field references are generated symbols, regenerating against the new schema and compiling gives an exhaustive list of call sites. Where queries are declared somewhere the layer can enumerate, parsing them all in the test suite turns a stale name into a build failure. Text search still works for literal query strings and for field tokens inside derived method names. What defeats every static method is text assembled from fragments at request time, so the inventory is only as good as the weakest style in the codebase - and I would say that plainly rather than promise completeness. Two runtime sources close the gap: the distinct statements an integration run emits, and the statements the engine reports having executed. Then I would still expand, migrate and contract, so a query I missed keeps working instead of failing the moment the migration lands.

go deeper

for a junior

Know that finding every query that touches a column is harder than changing the column, and that searching for the name only works when the query text is literal.

for a middle

Explain how each authoring style is enumerated - compilation, boot-time parsing, text search - and why runtime-assembled text escapes all three.

for a senior

Combine static and runtime evidence, state the coverage limits of each honestly, and shape the migration so a missed query degrades instead of failing.

for a principal

Decide what the organisation builds once - query validation in the build, a statement inventory, a rule about assembled text - so this stops being a per-change investigation.

## Why the inventory is the hard part Reshaping a column - renaming it, splitting it, narrowing its type, making it nullable - is a small change to the schema and an unbounded change to the code, because the code that depends on that column is spread across however many queries touch it. The migration is not the risk. **Finding every query** is the risk, and how hard that is depends almost entirely on the authoring styles in use. ## What each style gives you to search with | Where the query lives | How you enumerate uses of a column | How reliable it is | |---|---|---| | Typed DSL generated from the schema | Regenerate, then compile; or use the symbol's call hierarchy | **Complete** for anything that compiles | | Query declared by name in one registry | Read the registry, or let the boot parse it | Complete for what is declared | | Method-name-derived finder | Text-search the field token inside method names | Good - the name is literal, so it is findable | | Object-level query string in one place | Text-search the field name | Fair - depends on the string being literal | | Text assembled from fragments at request time | Nothing static finds it | **Poor** - the full text exists only at runtime | The pattern is that **an inventory is only as good as the weakest style in the codebase**. One helper that glues a predicate together from parts undoes the guarantee that every other query is compile-checked, because that helper's text never existed at build time for anything to check. ## The four moves that actually work 1. **Make the compiler do it where you can.** If field references are generated symbols, regenerate them against the new schema and let compilation enumerate the call sites for you. This is the only method that is exhaustive rather than best-effort, and it is the strongest single argument for a generated DSL in a codebase that expects schema churn. 2. **Parse every declared query in the build.** Boot the application, or run the layer's validation, inside the test suite. That turns "does this text still resolve?" into a build result for every query the layer can enumerate - no code generation required. 3. **Capture the statements the tests actually emit.** Log or record emitted SQL during an integration run and keep the set of distinct statements as an artifact. It catches the runtime-assembled queries that no static search can, in proportion to how good the test coverage is - which is the honest limit of the technique, and worth stating out loud rather than pretending it is complete. 4. **Ask the database what has been running.** Engines expose the statements they have executed. On a live system that inventory is real evidence rather than inference, and it covers code paths no test exercises - at the cost of only seeing what has run inside the retention window. ## Then make the change survive a miss Even with all four, assume you missed one, and shape the migration so a miss is loud and reversible rather than silent and destructive. The standard discipline is to expand, migrate, then contract: 1. **Expand.** Add the new column alongside the old one; write both from the application. 2. **Migrate.** Backfill, and move readers over one at a time. 3. **Contract.** Drop the old column only after a period in which nothing has read it. Any query you missed keeps working during the expand phase, and the read that still points at the old column shows up in the engine's statement inventory before you drop it. Contrast that with an in-place rename, where the miss becomes a failing request the moment the migration lands. ## What to build once, not per change - A **build step that validates every declared query** - the cheapest permanent upgrade in this whole area. - A convention that **no field name appears as a quoted string outside the data-access layer**, so the search surface is bounded and reviewable. - A **statement inventory from the test suite**, diffed on change, so an unexpected new statement shape is a review conversation rather than a production discovery. - A written rule that **runtime-assembled query text is the exception**, declared where the team can see it, because each instance is a hole in every inventory method above. ## What to say in the interview The strong answer names the ranking - compiler, then boot, then text search, then runtime evidence - admits that mixed styles reduce you to the weakest one, and finishes with the migration shape that makes a missed query survivable. The weak answer is "grep for the column name", which is only true when every query is a literal string in the repository, and is exactly the assumption that breaks on the one query built from fragments.

  • Why does one runtime-assembled query undo the guarantee from every other style?
    Because its full text never exists until a request supplies the parts, so no compiler, parser or search sees it. Static evidence covers everything except that one path, and that one path is enough to make an inventory incomplete. Treat runtime-assembled text as a declared exception, kept in a place the team can enumerate and review.
  • What are the limits of harvesting statements from the test suite?
    It only shows what the tests exercised, so it is coverage-shaped, not complete. It is still valuable, because it catches assembled text nothing static can see, and because diffing the statement set on each change turns a surprising new shape into a review conversation. Pair it with the engine's own record of executed statements for paths tests never take.
  • Why does expand-migrate-contract help even when the inventory looks complete?
    Because it changes what a missed query costs. During the expand phase both columns exist and both are written, so a stale reader keeps returning correct data instead of failing. The miss then surfaces as a read you can see in the engine's statement inventory before the contract step drops the old column - a warning rather than an outage.

saying these in an interview costs you the question

  • Says text search alone finds every query touching a column
  • Assumes compile checking covers queries assembled at request time
  • Trusts a statement capture from tests as a complete inventory
  • Renames a column in place instead of expanding then contracting
  • Forgets that the weakest authoring style caps the whole inventory
  • Skips the boot-time parse of declared queries in the build