Marcus Kazmierczak

Home mkaz.blog

Software Design in a World of AI

How we build and design software is obviously changing with LLMs. For the most part AI is creating the same software previously done by hand, same general principles and structure, just faster and more efficient. However, should it be creating software using those same principles?

If we know an LLM is going to be the primary tool to create, edit, and maintain the software then this should lead to different design choices. As Larry Wall famously put it, a good engineer is lazy, so previous software was designed in a way to make it easy for an engineer to make changes.

For example, to add a footer to each page, we use a common component or template. So when making an update it only has to be made in a single spot.

In early websites, running Apache, we would use server-side includes to handle this. You would register .shtml as a file that Apache should parse for an include. Then each html file you’d include a snippet like:

<!-- #include file="footer.html" -->

Now with LLMs, that wouldn’t really be needed. You could include the footer as plain old HTML in every file, and then when making an update, does it really matter if the LLM has to update 400 pages to say tweak the copyright date.

In this example, it would require a bit more token burn on the edit side, but on every page load, Apache wouldn’t have to do any extra work to serve.

CMS

At work, we are using a similar idea to replace parts of our CMS with Claude. We use a CMS to make it easier for the marketing team to update portions of the site.

However, onboarding marketing to the CMS system is an extra step, or three. They need a login, then learn a new system which is organized and tagged in different ways, and then update content and make sure using proper environment, etc. This is all unfamiliar.

However Claude is quite familiar. So we can create skills and a project in Claude to set them up, using GitHub/Vercel enables them to update the repo directly and preview their results easily with Claude as their editor. This is the same basic idea of adding the content they need to the files directly and serve.

This has the possibility to remove a whole piece from our infrastructure and make it easier for them to publish to boot.

Generators and Factories

This same idea probably can be applied to many parts of software design. Do we need more complicated factory design patterns, abstract classes, and inheritance?

The factory pattern is mainly used to prevent code redundancy, should that be a goal? How many times does the next implementation not quite fit and requires extending and complicating the factory making the code harder to understand as you trace through file after file of inheritance.

For example, take something like a PaymentProcessor factory that made sense for two payment types, then a third comes in that doesn’t quite fit, so someone adds a flag, then a subclass override, then a config object threaded through three layers — and six months later, understanding what happens when a payment runs means opening five files to trace the actual logic.

DRY

DRY, Don’t Repeat Yourself, is a long standing software design philosophy, so ingrained we rarely ask what it is optimizing for and does it still make sense.

React is like a desert, super DRY. React takes an HTML page and turns it into a tree of header, nav, body, footer, and every object on the page to its own component. Each of these components broken down into separate files, maybe as directories with another file for stylesheets. All this so that each piece exists in exactly one place, its style doesn’t bleed into others, it can be reused easiy. But now to understand a single page you have to walk a dozen files, keeping the shape of the tree in your head as you go.

Ugh! What a headache.

The design makes sense if humans are going in and editing a small piece, you can see the one component is in a single file making it easier to isolate and review. You don’t have to worry about cascading styles, or other parts of the page when working on the single component.

An LLM though can handle all that extra worrying with ease. It can update components and styles without impacting other parts of the system. You see this now with LLMs and the “apps” they are making are a single file with HTML, CSS, and JS all in one. Not because it’s a better architecture, but because it’s simpler for a machine. No modules, no build step, no tree to walk. The LLM can read the file top to bottom and have all the context needed to make any changes.

Summary

This isn’t a call to rip out every abstraction. Duplication still means every copy has to actually get updated. LLMs can still make mistakes. But it is worth reconsidering software design, a lot of the structure in how we built was around human limitations. Now might be a good time to check which parts of that scaffolding are still useful, and what are we maintaining just out of habit.