4 ms·
I always find these discussions interesting. I've been working for the same company 10 years. When we built project A, I was surrounded by people who had built
by nogridbag 2y ago
I always find these discussions interesting.
I've been working for the same company 10 years. When we built project A, I was surrounded by people who had built the same product for the past 20 years together. As a younger dev, I deferred to them as they were much older and had done this before. You would probably admire them. They pumped out code and delivered immense value. We had no over-engineering because there was no upfront design discussions. When we needed to use some service, we just wrote that directly in our normal framework code instead of using some over engineered abstraction. That's usually highly praised here on HN.
Years later, that project became an extreme maintenance burden. There is no separation of business code from framework code, so the first major framework migration took... years. When they completed the framework migration it was already 2 versions out of date. Subsequent upgrades were faster and given more priority, but still took multiple months. Onboarding new clients took whole teams. The code is fragile, lacks test, etc. The app is classic "spaghetti code". But they sure got to production quick!
Around 2.5 years ago, I was tasked with building project B. There was no development team. I worked with two domain experts, but ultimately the development of the product was in my hands. This is a product that will live on for at least another 10 years and will likely overtake our core product in revenue. There is no one to blame, but myself, if in 5-10 years this app becomes another unmaintainable mess. If you're in this position, what do you do? For me, all I could do is draw on my experience to try to avoid the mistakes I've seen made in the past. We made a lot of mistakes in project A, but I knew a big one was tying our business code, the core of our business, to the particular flavor of web framework that happened to be popular at the time the project started. How do you avoid that? Well, "Hexagonal architecture" sounds like a big scary buzzword, but it's a damn simple pattern that solves that elegantly. When we started, I basically received an indirect mandate that we use the same framework as project A. After we had written ~25k LOC, I had enough and we dropped that legacy framework and migrated to something much better. And we didn't have to change a single line of our business code. Our new framework allows us to iterate much faster than legacy framework. 2.5 years in and our code looks as clean as that initial POC I built. Hexagonal was a small bit of the initial architecture I did. I only point it out here as a counter example. But all the little things I've done which are considered "architecture" have paid off years down the road.
- hu3 2y agoInteresting. I'd like to think that, most probably, your job exists and is funded because those folks (and you) "got shit done" for the last 20 years. In my experience (around 2 decades also), when it comes to small/medium projects, spending a bit of effort separating business logic from UI/database/server code is wise up to a certain point. If the effort doesn't add too much code and layers of indirection, then fine. However, DDD/HexagonalArch consultants tend to add soo much boilerplate and layers of indirection between between an HTTP request and a response that it becomes detrimental to productivity. For example take a look at this article: https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together https://herbertograca.com/2017/11/16/explicit-architecture-0... Specially this part (but the entire article is a gem): > This means that Dependency Injection and Dependency Inversion are not enough to decouple components, we will need some sort of architectural constructs. We might need events, a shared kernel, eventual consistency, and even a discovery service! Respectfully I'd like to think that it is satire. But it isn't. With events your code is no longer linear. Stuff happens with triggers (events) that are not clear at first glance unless you fully understand the entire stack. Shared kernel is just a place to shove interfaces under the carpet and pretend code is no longer coupled because you added extra 2 layers of indirection (interfaces and kernel) between 2 lines of code. Congrats now it became much harder to understand what actual code will be executed when I call this method. Eventual consistency... Is that a good thing? We spent 4 decades maturing ACID compliant transactional RDBMS only for someone to come and disregard all these gains? Discovery service... Yeah your code became so complex that you need more code to discover code. Because we can't have one layer calling another directly. Nonono, that would be coupling. And coupling makes it hard to change frameworks in a distant but slightly possible future. I'd rather pay a very expensive insurance from now on to safeguard from that possibility. Even though the insurance itself is so expensive that it alone can suffocate the project. Sorry for the rant.
- nogridbag 2y agoHaha, I read through the beginning of that article which focused on hexagonal. While it was an over-complicated example, it didn't look too bad. But then skipped ahead to your quote. It definitely felt like satire to me! (Or at least I hope it was!). And that's where a balance is needed. No one seems capable of defining what good architecture is. We have plenty of posts about spaghetti code, architecture astronauts, etc. But there's few comments with success stories, especially about fixing a spaghetti codebase and what that project looks years down the road. And I think that's because the problem is multi-faceted. It's not "just" a technical problem. From a technical side, it's primarily lots of little patterns that as a whole add up to producing quality code. But there's also the social side, to hiring, reining in devs, enforcing the code patterns and quality, pushing back on business, etc. I can give lots of examples, but this would be a long post. All of these factors ultimately determine the fate of the project, the quality of the code, and whether it becomes spaghetti code or an over-engineered mess.