The Input Box Built for a Proposal, Rated to Hold Four Characters

Somewhere in my warehouse app, there’s a text box roomy enough to propose marriage in. It’s rated to hold four characters.

The database schema was unambiguous on this point: order numbers, twenty characters, max. Something like FUL-2026-000482, short, fixed-format, structurally incapable of growing past twenty. Nobody told the input box. It rendered full browser width, edge to edge, the digital equivalent of handing someone a legal pad to jot down a phone number. I typed four digits into it and watched the cursor sit there in all that leftover real estate, doing nothing, judging me quietly.

Then I scrolled down to check the rest of the form. And kept scrolling. Every field got the identical treatment: a tiny label perched on top like a garage sale sign, and underneath it, a box roomy enough to draft a will in. Warehouse names. Carrier codes. Status dropdowns. All of it stretched out edge to edge like the page was building anticipation for something enormous that was never, ever coming.

My exact reaction at the time: there’s a difference between minor GUI tweaks and obvious yuck. This form had cleared that bar with room to spare.

Here’s the actual root cause, and it’s a boring one. mix phx.gen.html gives you a fully working, entirely unstyled scaffold: routes, controller actions, changeset validation, form fields wired to the correct database columns. It does not, and was never going to, guess how wide a twenty-character field should render on a 1920-pixel monitor. That’s not the generator’s job. Layered on top of that, I’d generated warehouse pages and order pages at different points in the build, and only the warehouse templates picked up the shared <Layouts.app flash={@flash}> wrapper. The order pages didn’t, so two sections of the same app were quietly wearing two different outfits, and nobody had told them they were supposed to match.

Here’s where I actually stopped myself before fixing it out of pure embarrassment, and sat with the bigger question instead: should I have pushed harder on this earlier? Two weeks building this thing, and every single hour went toward the part that’s genuinely difficult. A GenServer per order, holding live in-memory state. A DynamicSupervisor watching every one of them, ready to restart a crashed process under a new PID before you can blink. A Registry mapping stable order IDs to whichever PID currently happens to be running that order, since PIDs change on every restart and nothing in the codebase should ever have to know or care. PubSub decoupling the pipeline entirely from the UI, so a dashboard can subscribe to “orders” and re-render the instant something changes, with zero JavaScript written by hand. A crash-recovery test that kills a live process on purpose and asserts the Supervisor brings it back with correct state, restored from Postgres, in under fifty milliseconds.

Not one of those hours went to input widths. And the more I sat with it, the more I think that was the right call, not a shortcut I got away with.

Here’s the actual argument, and I think it holds up: we’ve all seen the flawless, pixel-perfect portfolio app by now, the one that looks like a design agency built it, that turns out to be a beautiful shell with nothing real running underneath. Polish is the cheapest thing AI generates on command. Ask any current model for a beautiful login form, and you’ll have one in ten seconds, complete with a gradient nobody asked for. That used to be a signal of competence. It isn’t anymore, because it’s no longer expensive, and hiring managers who’ve been paying attention already know that. An ugly form sitting on top of a Supervisor that actually supervises, tests, and passes is a stronger tell of a real engineer than a gorgeous one sitting on top of a prayer and a hope nobody restarts the server.

So no, I don’t think I should have pushed back and demanded more Tailwind polish mid-build. This project’s entire stated purpose, on its own portfolio page, is demonstrating fluency with OTP architecture and fault-tolerant system design, not demonstrating that I can center a div. If I’d spent build time getting input widths right instead of writing the crash-recovery test, I’d have a prettier form sitting on top of a thinner architecture story, and thinner architecture stories are exactly what stop getting callbacks once someone actually reads the code instead of just admiring the screenshot.

Fixed the input widths anyway, for the record, once the parts that actually mattered were done and tested. Real width constraints per field, based on what the column can actually hold, not what the terminal happened to default to. Consistent max-width wrapping across every generated page, so order numbers stop breaking mid-word. Labels sized to sit next to their inputs instead of hovering above them like a warning sticker. Twenty characters now gets roughly twenty characters worth of box, and the page stopped requiring a scroll-based cardio routine just to reach the Save button.

But I fixed it last. On purpose. After the Supervisor, after the Registry, after the test that proves the whole thing survives getting killed on purpose. In that order, because that’s the order that actually matters when someone’s deciding whether to bring you in for an interview.