What we optimise for when we reach for an LLM Bellroy CTO Andrew Bell argues that LLMs should be evaluated by long-term ROI, not just code generation speed, and that using them to draft boilerplate is wasteful while using them to implement features from well-defined types is valuable. Bellroy, a profitable physical-product company with an internal engineering team, treats technology as a profit centre, prioritizing maintenance costs and business collaboration over shipping code quickly. Disclaimer: I wrote this blog post entirely by hand, but used Claude to review and provide feedback on it before seeking human review. Most of the blog posts I read about LLMs are either gushing about a new era of infinite productivity, or bemoaning the enshittification of the entire software industry. I don’t often come across posts that take the even-handed view of evaluating these tools like any other tool, so I thought it was time to make a contribution to the discussion. If I discovered one of our engineers repeatedly typing out boilerplate for JSON decoders, I’d be disappointed. If I discovered them spending an afternoon hand-crafting types that encode our business invariants into our systems and then letting LLMs draft the feature based on those types, I’d be delighted. The aim of this post is to communicate why I’d react differently in each circumstance. Our context and why yours may differ The lens I’m bringing to this has been shaped by the kind of company Bellroy is and the kind of CTO I strive to be. Bellroy is not a software company; it’s a physical-product company that happens to have technological proficiency as one of its core competencies. Since the very early days, Bellroy has employed an internal engineering team - initially to focus on automating the order fulfilment pipeline, but today to build and maintain an entire bespoke e-commerce platform. Bellroy has been continuously profitable since early in its history and isn’t sprinting towards the end of a funding runway. This substantially influences the way decisions are made day-to-day. One of Bellroy’s goals is sustainable, ethical profitability. To meet that goal - among other things - we must be evaluating choices based on long-term Return On Investment ROI . That evaluation does a lot of heavy lifting: it means that the benefit of shipping more code more quickly must be offset against the cost of maintenance. This philosophy neatly fits my approach to running the Technology team at Bellroy: we must operate as a profit centre, not a cost centre. Engineering as a profit centre Some concrete examples of “profit centre” thinking in action: choosing statically typed functional programming languages /our-technology-stack-and-how-we-got-here.html to eliminate entire classes of production issues- prioritising regular dependency updates of every single code repository we manage to avoid “drop everything” zero-day vulnerability scenarios and dependency rot - employing a “fixed time, flexible scope” project methodology /using-shape-up-what-works-what-weve-changed-whats-next.html to eliminate project “overhang” while still delivering tangible, measurable outcomes - using internally-built probabilistic Net Present Value NPV https://www.investopedia.com/terms/n/npv.asp calculator tools that use Monte Carlo simulations to evaluate project benefits another blog post, perhaps , and using those valuations to drive prioritisation decisions and push back on scope This last point is important - we rely on our engineers not only to ship features and maintain our codebases, but also to help project stakeholders to identify what they actually need as opposed to what they asked for. Technology does not operate as a silo in Bellroy; we collaborate with other functional teams to deeply understand their processes - and how changes to their processes impact the rest of the business - before we write a single line of code. This is where we find the current generation of LLMs still wanting. The “everyone’s a builder now” mentality sounds great if you believe that generating code is all benefit and no cost, or if it lets someone adept at business analysis - who has already validated the ROI of a particular change - bypass the engineering department and ship the thing themselves. That perspective ignores the opportunity cost of the next-best-thing that person could have been doing with their time, and the maintenance cost of the thing that the Technology team now maintains but had no part in designing. “But Mike”, I hear you say, “surely the LLMs can do the maintenance and bug fixes for all those vibe-coded features as well?” Well, yes, you’re partially right. We do use LLMs for maintenance and bug fixes. But what we don’t do is rely on LLMs to resolve those rare-but-critical production issues at 3am on a Sunday morning. With so many horror stories https://www.theguardian.com/technology/2026/apr/29/claude-ai-deletes-firm-database floating around the internet of what unmonitored LLM tools can do with access to production infrastructure, we are unwilling to give them write access to ours. With this responsibility still in the hands of the humans, the humans need to have a good mental map of our systems architecture and our infrastructure. We need to be tracking how big the on-ramp is for new staff. And that’s exactly what we’d give up by ceding all engineering activities to the LLMs. What maintainability actually means to us and Claude We use Claude https://claude.ai/ at Bellroy, across the entire company. And it’s great for so many things. One thing we’ve noticed about Claude-generated code is that Claude tends to want to provide verbose comments on most changes it makes. Despite explicit instructions not to do so, it uses these comments to document what the code does, or adds context from the prompts, or refers to previous states of the code that become irrelevant the second a change ships. For us, this is bad practice. Code comments should only provide context that is current and that is not derivable from just reading the code - adding a comment that just describes what the code does increases the risk of the documentation diverging from the implementation, which just adds potential confusion for no real benefit. Our function, type and variable names should do enough of the work to allow an engineer to get oriented. We use comments only to provide the “why” of a particular implementation choice so the intent - the “should” of the change - is preserved, because it is that information that is hardest to reverse-engineer from the code one year from now. We take a similar approach to pull request descriptions, using a template that asks for a problem statement what problem exists prior to this change , a solution description how this particular change solves the problem and an account of what the engineer has done to verify that the problem is actually resolved. Without engineers having taken part in the scoping process and understanding the size and shape of the business problem, they can’t provide that context. Another deliberate decision we’ve made to assist with maintainability is to work in a monorepo https://monorepo.tools/ . This means that in our commit history, related changes are often shipped together. Coupled with the practices described above, this - at least in theory - should mean that an engineer working in a particular area should be able to answer 3 questions: - What does this particular area of the code do? - Why - from a business perspective - does it do it? And why in this particular way? - What else had to change to make this solution workable, and