Vibe Engineering: Solving Small Cross-Cutting Concerns A developer with experience in AI across oil & gas and healthcare describes building a high-end PC to run local LLMs and diffusion models, and joining a new company in late 2025 to tackle complex codebases. The engineer uses AI coding tools as interactive assistants while performing most work manually. This article was originally published on my blog . I've been enamored by AI technologies far before the current generation of generative large language and diffusion image models hit widespread popularity in the early 2020s. Early in my career, I worked as a developer for a local Oil & Gas focused software company. One of the wings that they ultimately spun off into its own organization revolved around performing predictive data analytics on the massive datasets we'd accumulated across our various customers. Eye-opening stuff. Through properly trained models, the company was able to predict all sorts of failure modes, workflow trigger-points, and actionable business insights. The types of decisions that could be made off of these seemingly meaningless data streams changed how I thought about what software could do. After I left that company, I joined a local healthcare organization. We built incredibly insightful visualizations, reports, and dashboards with our wealth of data. We only dipped our toes into training models for actionable predictions, but even those small exercises proved worthwhile. Outbreak forecasting built atop our wide-reaching care network let us glean and plan for valuable resource allocations months in advance. A few years later, I found myself working back in oil and gas, this time at a company in a different sector of the field. They too were leveraging AI, training models on pipeline defects, failures, and inspections. This was their bread and butter. Every advancement yielded dividends. As the clock ticked ever forward, I landed at yet another organization right as the first real generations of AI-assisted coding tools hit our IDEs. Software focused, this company planted its stake alongside giants like OpenAI's ChatGPT with our own growing suite of AI-assisted offerings. Once again, the capability of these technologies captivated me. Around this same time, I started really tinkering with local Large Language Models and diffusion-based image generation. Ollama. Stable Diffusion. Anything I could get my hands on. I was excited enough about the possibilities that I built myself a new PC: a GeForce RTX 4090, 128gb RAM, an Intel i9 14900k ouch , and a whole bag of other goodies. I spared no time getting the machine setup with tooling, utilities, frameworks, and libraries. As the various coding models grew more capable, I found myself leaning into them more, allocating larger and more nebulous tasks their way. For the most part, I used them like a friendlier, more interactive StackOverflow compatriot. I still performed the vast majority of the actual work. Then came the last quarter of 2025. I received an offer from another local company. I wasn't exactly looking, but I'd heard great things about the organization. They serenaded me with songs detailing near-endless problems in need of targeted, efficient solutions. Never one to shy away from an interesting challenge, I took the plunge. I quickly found myself back in the office, up to the eyeballs in new codebases and problems I'd never quite encountered before. Sure, the ingredients were familiar. I'd seen variations of these problems throughout my career. But rather than cooking exquisite enterprise dishes, I found myself staring at gas station spaghetti. To my coworkers' surprise, I love spaghetti. Paradoxically, perhaps. It was one of my favorite dishes growing up. Unless there was lasagna on the menu, you could rest assured I was getting spaghetti. I loved it so much that I learned to cook it from scratch before I could competently solo-read a Harry Potter novel. Unsurprisingly, spaghetti code was also my first approach to programming, many moons ago. And here I was, hailing from the hometown of the late Tulsa Spaghetti Warehouse, staring back at an all-you-can-eat buffet. So I got to chomping. Bite after bite, I picked up forks-full of noodles and slurped them up. Meticulously. I started putting names to the dishes. Angel-hair. Spaghettini. Spaghettoni. At the risk of beating an already horrifically abused metaphor, I continued tearing apart one dish at a time. Every second of downtime between onboarding tasks became an opportunity to absorb as much as possible. Not just from the business perspective, but more importantly, from the architectural view. The software was home-grown, home-cooked, home-eaten. We had to internally digest it all. The team had mostly come up through the company, many having been there for 15, 20, 30+ years. The code had evolved alongside the company and the individuals who shaped it. Invaluable knowledge lived within those repositories. But it was unstructured. Unrefined. There were and still are hundreds of codebases comprising the enterprise stack. They're managed by separate and overlapping teams, all focused on propelling the business forward. As the company grew, so did the code. As requirements changed, so did the implementations. Littered throughout various repos, you'll find comments. Some truthful. Many lying. All purporting to guide you through a forest of overlapping, stateful, static, threaded operations that seem to have sprouted at random. You clone. You touch. You grep. You cry. You prototype. You bargain. You claw your way through discombobulated systems until, slowly, light begins to shine through the ever-thinning limbs. And you start to make sense of what you're looking at. Not a forest. No. Something similar, but different. Something closer to a series of massive, interconnected, cautiously cohabitating fungal colonies. Myriad mycological monstrosities expanding into every suitable niche upon which their spores land. As you delicately disentangle their interwoven mycelium, the operation turns into something like a vivisection. Laid bare on the table, the endless nerve-like tendrils begin to make some semblance of sense. Slowly but surely, different domain concerns start growing defined boundaries. Lists upon lists are made of identified cross-cutting concerns, domains, state mechanisms, authoritative sources of truth. Chaos soon begins to look a lot like patterns... Architectural building blocks and design patterns are nothing new. Hundreds, if not thousands, have been coined over the years. One of the "bibles" on the subject, Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vissides, came out while I was still largely quadrupedal. I was probably just taking my first few steps. Despite the antiquity of such wisdom, employing these practices isn't the default operating mode for most developers. This continues to surprise me. Most people don't work in the field of their passion. They don't go home and continue thinking and dreaming about their topic of study. Instead, they chase the highest paying fields or follow whatever aptitudes they happen to possess. Silly things like that. For better or worse, I was never instilled with such predeterminations. Ever since discovering programming in my pre-tweens, I've been intent on wielding that hammer to sink every nail I can find. Over that multi-decade endeavor, I've hit a lot of nails. Also screws. Staples. Thumbs. Drywall. And other imperfect targets. Through that repeated practice and self-injury , I've managed to beat in a few hard-earned lessons: Anyone who's been writing code for longer than an industry-standard release cycle knows things change fast. One week everyone's shoveling the latest ECMAScript hotness down your throat. The next, you're in an intense battle between Rust, Zig, and a bottle of dark liquor. Frameworks, languages, and libraries are a bit like running down to your local big-box hardware store trying to settle on power tools. Certain tools are fantastic for specific problems. You'll find more or less capable versions of similar tools across different brands. The choice matters, but not as much as people think. It's often stated that premature optimization is the root of all evil. Agree or disagree with that sentiment, I believe most can agree on one thing: a problem solved via an imperfect solution beats the problem existing unabated. What hasn't changed in the 60+ year span of our field? The components of good architecture. From the earliest beginnings, our tooling, languages, and approaches grew around the problems code solved and the strategies used to solve them. Put whatever label you want on the organization: Clean, Hexagonal, Vertical Slice, Domain Driven. The core tenets of creating declarative, functional, problem-domain-centric solutions have held steady through a blustering whirlwind of technological innovation. I want to linger on "declarative" for a moment. It's become central to how I think about code quality. Declarative code describes what should happen, not how it happens. It reads like a specification rather than a set of instructions. It's flat rather than nested. Composable rather than procedural. Consider the difference: js // Imperative: a sequence of instructions var user = GetUser id ; if user = null { var permissions = GetPermissions user.RoleId ; if permissions = null && permissions.Contains "admin" { var report = GenerateReport user ; if report = null { await SendEmail user.Email, report ; LogSuccess user.Id ; } else { LogFailure user.Id, "Report generation failed" ; } } } // Declarative: a description of what should happen await Given userId .Select GetUser .Where user = user.HasPermission "admin" .Select GenerateReport .Match some: report = SendEmail report .Then LogSuccess , none: = LogFailure "Report generation failed" ; The declarative version is flatter. No nesting. Each step is a transformation or filter. The flow reads top-to-bottom without branching into ever-deeper indentation levels. You can understand what it does by reading the method names. No need to trace through conditional logic. This isn't just aesthetic preference. Flat, declarative code is easier to test because each step is a pure function. Easier to modify because you can add a step without restructuring everything around it. And crucially for our purposes, it's easier for agents to produce correctly. An agent generating the imperative version has to track state, manage nesting levels, handle the combinatorial explosion of possible paths. An agent generating the declarative version? It just chains transformations. Package it however you like. As long as your solutions adhere to fundamental software engineering principles, your code can transcend whatever mortal coil its ecosystem imposes upon it. How can one begin to design a system if they don't know what they're building? Too often I see developers blindly feeling their way through a codebase. They coordinate internal and external systems, real and simulated hardware, debuggers, and various other tooling just to pinpoint where they need to work. Then, once they've settled on their platform, they start blindly smashing atoms together hoping to yield something usable. Experimentation is great. It's one of the foundations of our field. But experimentation is not for production work. That's not to say you can't conduct experiments in production. I have an entire framework dedicated to that cross-cutting concern. But writing and performing experiments around the periphery of your implementations? That's the duty of your testing framework. If you find yourself probing instead of going directly to the test encapsulating your concern, you're working in a malignant, terrifying codebase. Rather than having a system shaped around external business concerns business-provided inputs, service-provided outputs , you have a black box that requires action to validate. Like trying to guess the components of a modern art shadow projection by staring solely at its stain upon the wall. When a developer needs to write code within an application, be it a new feature, bug fix, or otherwise, it's their duty to ensure they have clearly laid out expectations. Paramount among these: expected inputs configuration, user inputs, workflow steps and expected outputs actions performed, values returned, errors that can be thrown . If experimentation is necessary to determine these parameters, that experimentation should be specific, documented, time-bound, with concrete deliverables. Those deliverables should then be cemented through business-relevant behavior-driven tests that enshrine the knowledge gained and mirror the documented requirements. I've learned a lot of skills over the years. Varying usefulness. If you need your floor tiled or your cat needs a crocheted vest, I may know a guy who can give some pointers. But despite my obsessive compulsion to absorb information like a precious commodity, I lack any desire to repeatedly do things. Don't get me wrong. I can. I've been writing code for decades. But repetitive, monotonous tasks bore me to tears. So I've always tried to learn how to do something, improve it in some way, quantify it, and then automate it as much as humanly possible. I'm prone to overusing metaphors. No exception here: I never want to do the digital equivalent of drywall. Placing, finishing, and painting drywall are all technically simple endeavors on their surface. Give a sufficiently motivated greenhorn a couple weekends and they'll put up work that competes visually with career tradesmen. That says nothing for their speed or accuracy, or their need to redo tasks as they learn. But the finished product can ultimately be indistinguishable to an end customer. Thing is, like many general contractors, I never set out to master one skill. I went out to solve problems and move onto the next. As I found more problems, my skills refined. My approaches improved. In isolation, that's a sustainable model. But over time, across many organizations and codebases, you'll invariably find yourself solving the same problems at a different place with a different toolset. You'll be putting up drywall, mud, and paint rather than selling and refining your new array of drywalling, mudding, and painting robots. When solving problems, the work is rarely done in a vacuum. Developers start to get an itch that they've seen something before. I implore you: don't ignore it. Don't just copy and paste the solution that worked last time into the new class with a couple tweaks. Step back. Solve the bigger problem. And this is where the modern era offers us something genuinely new. AI agents have amazing potential. But like overzealous, over-studious summer interns, they lack refinement, direction, and context. That load-bearing poster keeping this place standing for the last 20 years? To them, it's just ugly, dated decor of a bygone era, ripe for paving over to make their mark. Within their sponge-like monolith of training data, they've accumulated vast wealth of knowledge. But it's locked away behind ear worms, secret incantations, and raw willpower. These agents are dang near capable of solving any singular task. Unlike the fleshy meatbags that wrote them, though, they lack the criticality, forethought, and analysis paralysis that keeps us humble. That ongoing game of chicken with imposter syndrome? They don't play it. Once a problem becomes sufficiently large, it usually becomes multiple problems. Like a cancer metastasizing through the body, problems grow. They multiply. They cripple otherwise healthy solutions. Understanding how to leverage agents to break your problem down and distill it into its most basic essence? That's the key. You cannot let them behave like chemo, attempting to find and fix every issue at once. You have to create a single, solvable, provable domain. Then you set them to work there. The scope and scale of that domain will vary. Enterprise organizations have their own platforms. Their own logging wrappers, observability, configuration, options, feature flagging, messaging, and so much more. The second something leaks into multiple applications, it's time to figure out how to carve it out. Once a standard has been set, it's much easier to keep it contained, provable, and validated. I keep coming back to the intern metaphor because it's genuinely the most apt comparison I've found. A fresh intern shows up on day one with a head full of textbook knowledge. Four years of academic projects. And absolutely zero understanding of why your production database has a table called tbl Users OLD DONT USE 2019 . They're capable. They're eager. They will absolutely demolish your carefully maintained naming conventions if you don't give them explicit guidance. Agents operate the same way. They've "read" more code than any human could in a lifetime. They know seventeen different ways to implement a repository pattern. They can recite the Gang of Four like scripture. But they don't know that your company uses ICommandHandler