@leon74
Trying to put engineering back into software development
I specialize in developing object-oriented java applications that aligns with business objectives, using Domain-Driven Design principles to ensure technical decisions drive tangible value. By focussing on a deep understanding of the business domain, I craft solutions that solve real problems while maximizing ROI. My approach evaluates the cost/profit ratio of every decision—only implementing technologies when the benefits outweigh the costs. I’ve been called in to revive stalled projects and address challenges where others have struggled. My focus is on creating software that not only meets but exceeds business expectations. Whether working with legacy systems or modern frameworks, I select the right technologies to maximize value—not just follow trends. I believe software should be a strategic asset, and this mindset guides every decision I make in development.
Architecting maintainable java applications, domain-driven design guidance, codebase refactoring, strategic technical direction, and high-leverage Java development.
Hi Chisomo, really appreciate the kind words, glad it resonated. You’re asking the right question, and also a slightly dangerous one 🙂. That's because it’s tempting to answer it with a reading list, while the thing you’re trying to learn is not primarily learned from books. Domain modelling is much closer to a craft (like pottery, or architecture) than it is to something you can absorb passively. Books can give you vocabulary and examples, but the actual skill develops when you try to structure something yourself and feel where it resists. That said, a few things are worth reading, just not in the way people usually recommend them: Domain-Driven Design: Tackling Complexity in the Heart of Software — mainly for the idea of ubiquitous language. That chapter is foundational. The rest is useful as reference, but I wouldn’t try to “apply DDD” as a method. Design Patterns: Elements of Reusable Object-Oriented Software — not because patterns make you a good modeller, but because they give you a vocabulary for expressing structure once you start seeing it. The Mythical Man-Month — older, but still one of the clearest explanations of why conceptual integrity matters. More important than reading is how you approach your day-to-day work. A few practical starting points: Try to avoid “fat services” that orchestrate everything. Ask yourself: which object actually owns this behaviour? Then move it there. Let objects own both state and behaviour. If something has behaviour but no state, that’s fine — it can still be an object that owns that responsibility. When something feels awkward to implement, don’t immediately work around it. Treat that friction as a signal that your model might be off. If you want something more hands-on, there’s an old exercise that still works surprisingly well: take a simple domain (orders, invoices, etc.), assign each concept to a person (or even just objects on a table), and “play through” a scenario. Each participant is only allowed to act within their responsibility. It sounds simplistic, but it forces you to think about boundaries in a very concrete way. The goal at this stage isn’t to “do DDD correctly”. It’s to start noticing the difference between: putting code where it’s needed, and putting behaviour where it actually belongs. Once you see that difference, you’ll start seeing it everywhere. If this way of thinking clicks for you, I write more about it here as well: https://blog.leonpennings.com It’s essentially an extension of the same ideas from the article, just explored from different angles.
The real question is whether better tooling / future AI will close this “understanding gap.” I believe the answer depends on the scale of the application: For small, relatively simple applications (roughly up to 20-30 user stories), tooling and current AI can already replace most traditional development. The domain is shallow enough that deep modeling isn’t critical. For larger, long-lived enterprise systems, I don’t think better tooling alone will be enough. The gap isn’t just a matter of more context or better prompts. Building a good domain model requires imagination, conceptual invention, and iterative discovery through friction, the very process that current AI architectures bypass. AI generates structure quickly, but it doesn’t hold intent or accumulate nuanced understanding the way evolutionary modeling does. Until AI can actively perform real domain modeling (discovering natural concepts, responsibilities, and boundaries), the most effective approach remains strong human engineering judgment paired with AI as a powerful accelerator for implementation. So yes, tooling helps a lot. But it hasn’t replaced engineering in complex domains, and I’m not convinced it will anytime soon.
For smaller applications, AI can often handle the full cycle effectively. For larger, long-lived systems, though, a coherent domain model and deep context become critical. In the evolutionary modelling approach, building the code is the learning process. The friction, the failed attempts, the moments when a concept doesn’t fit, these aren’t obstacles. They are the essential feedback mechanism that refines your understanding of the domain. The code becomes both the map and the territory. AI breaks that loop. It’s like having someone else read War and Peace for you and then giving you a detailed summary. You might grasp the broad plot, the Napoleonic wars, the fates of Pierre, Natasha, and Andrei, but you miss the emotional depth, the evolving character arcs, the philosophical layers, and the subtle interconnections that give the story its real power. In small applications, those nuances often don’t matter much. In complex systems, they are everything. The accumulated understanding of why things exist, how responsibilities truly relate, and how the domain behaves under pressure lives in those details. When you outsource the building, you also outsource the learning. That’s why the 85%; the structuring, naming, and boundary finding, cannot be fully delegated. It’s not just design. It’s domain acquisition through the act of building.
Thank you — I appreciate the kind words on framing refinement as a shared thinking activity. That’s exactly the shift I’m advocating for. That said, I believe the deepest value in refinement isn’t in producing better-structured drafts or accelerating the breakdown of tasks (as useful as those can be). As Fred Brooks made clear in No Silver Bullet, the hard part of software engineering has always been the essential difficulty: building the complex conceptual structures and truly understanding the problem domain — not the accidental difficulty of expressing it in code or organizing the work. In refinement, the highest-leverage signal isn’t how cleanly we can structure a story or generate implementation details. It’s whether the team has surfaced and aligned on the real business need: What part of the underlying domain model actually needs to change? Is the stakeholder asking for ‘A’ in the story, but the outcome they truly want is ‘B’? Are we evolving the shared mental model of the business, or just adding another feature on top of yesterday’s assumptions? Tools can (and should) help with the mechanical side once understanding is solid. But if refinement becomes primarily about structured drafts or AI-assisted task breakdown, we risk optimizing the wrong thing — accelerating implementation of a still-fuzzy understanding instead of refining the mirror that lets the business see itself more clearly. In my experience, the best predictor of smooth sprint planning isn’t refinement quality in terms of structure, but refinement depth in terms of shared conceptual clarity about the domain and intent. That’s what reduces volatility downstream — not better scaffolding, but better truth.