When a working feature moves from one app to another, the structure and the logic transfer. The constants do not, because every number is a measurement of the place it came from.
Across the apps I build there is a standing rule that a new app starts by copying the screens that already work in an earlier one. Messaging, the bottom tab bar, the calendar, the search filters, the native shell: each of those was paid for once, in time and in bugs, and there is no reason to pay for it again. Different niche, different palette, same layout and same logic. The class names come across unchanged so the two apps can be read side by side.
That rule has a second half, added after the first half went wrong: copy the logic and the structure; never copy a constant. This piece is about why the numbers are the one part of working code that does not travel.
The messaging thread in Tegula Stone sizes itself with calc(100dvh - 190px). The
value is correct. In Tegula the document itself scrolls, and the thread has to fit in exactly the
space left once the header, the composer and the tab bar have taken theirs. Someone measured those,
added them up, and wrote down the total.
When the thread view was copied into Care Royal, the rule and the value came across together. Care Royal's shell is built differently: the document is held still and an inner container scrolls. Dropped into that layout, a thread sized against the viewport created a third nested scroll region inside the other two and pushed the menu bar out of place. Nothing about the CSS was wrong. It was correct about a page that was not the page it was now on.
What makes this worth writing about is that the logic copied perfectly. The inbox and thread as two separate screens, the back control, the message list, the composer pinned to the bottom — all of it transferred without change and worked. The only thing that broke was a single number, and it broke because it was the only part of the feature that was a measurement rather than a decision.
Logic generalises because it describes relationships: the composer sits below the messages, the
list scrolls and the header does not. A constant describes a particular instance of those
relationships. 190px is the sum of three heights in one layout, and the expression that
produced it — header plus composer plus bar, in a document that scrolls — was thrown away
the moment someone typed the answer.
That is fine inside the app where it was measured. The premises are still true there, and if they change, someone will probably notice the thread misbehaving. Copied elsewhere, the number arrives without its premises, and looks exactly as authoritative as it did at home.
Pixel offsets are the visible case. The same thing is true of nearly every constant that looks like configuration:
Every one of these will usually still “work” after copying, in the sense of producing no error. That is what makes them dangerous. A wrong function tends to fail visibly. A wrong number fails as a slightly off experience, on a particular device, at a particular size, with the keyboard open, for the users you are least likely to be.
The method is short, and every step exists because skipping it caused a real problem.
Class names, element hierarchy, the state machine, the order of operations. Do not improve them on the way across. The value of a copy comes from being recognisably the same thing: when the original gets fixed, the fix can be applied to the copy by reading one against the other. A copy that was tidied in transit can no longer be compared, and every future fix has to be rediscovered.
For each number in the copied block, write down what it means, then work out what that meaning
comes to in the new app. Where possible, replace the number with the expression that produces it, or
better, with a layout that does not need it at all. A thread that is a flex child with
min-height:0 inside a container that already has the right height needs no arithmetic,
and so has no arithmetic to be wrong about. The best outcome of re-deriving a constant is often
deleting it.
Comments that explain why are the premises the constant lost. They are the only record of what the number was measured against, and they are the part that stops the next person from “cleaning up” something that was deliberate. A copied block with its comments stripped is a block that looks arbitrary, and arbitrary-looking code gets changed.
If the original has a magic number with no comment, that is a defect in the original. Write the comment there first, then copy it.
Copying is a second review of the source. Reading a feature closely enough to transplant it is the most careful reading it usually gets, and it regularly turns up bugs in the original. A bug found while copying is a bug in the source, and it gets fixed there as well as in the copy. Otherwise the two apps diverge in exactly the places where one of them is known to be wrong.
The commit that brings a feature across says which app it came from. The copied CSS lives in one contiguous block headed with its source, rather than being scattered through the target stylesheet. Both exist so that anyone can later put the two side by side and see what is shared, what was deliberately changed, and what drifted.
The obvious objection is that all of this is what a shared component package is for. Write the thread once, import it twice, and there is nothing to copy.
For a small portfolio I think copying with lineage is the better trade, and it is a trade rather than a free win. A shared package couples the release cycles of the apps that depend on it. Two products at different stages, with different shells and different scroll models, would need the component to be parameterised for both, and the parameters are precisely the constants discussed above, now hidden behind an interface. A change made for one app becomes a regression risk for the other, which makes every change slower.
The cost of copying is drift, and drift is real. I wrote about it in two footers, one site: copies diverge without anybody editing anything wrongly. What keeps it manageable is the discipline above. Identical class names make the copies diffable. Contiguous blocks with a named source make them findable. Fixing bugs in both places keeps the shared parts shared. If the number of copies grows to the point where that stops being tractable, that is the point at which extracting a library pays for itself, and not before.
The general version is simple enough to hold in your head while copying anything. Decisions travel: what goes where, what scrolls, what happens on tap, what order things are written in. Measurements do not: how tall, how long, how many, how often. The decisions are why the original works. The measurements are why it works there.
So when you lift working code into a new home, the questions to ask of each line are different. Of the logic: is this the same problem? Of the numbers: what was this measuring, and is that thing the same size here? The second question takes longer, and it is the one that would have kept a menu bar where it belonged.
Written by Liana Grigory, Entrepreneur and Software Engineer, from work on The Care Royal, Tegula Stone and Unified Savers. Everything above describes decisions actually made on those systems, including the ones that turned out to be wrong.