DHH is on to something with his recent post on HTML over the wire. It's high time that we start tearing down this gigantic mount of complexity that we've built the last two decades and get back to application development that developers can actually understand and excell with!
Showing posts with label architecture. Show all posts
Showing posts with label architecture. Show all posts
Saturday, February 13, 2021
Thursday, January 30, 2020
The Modular Monolith
Having been a micro-services sceptic from the start, and a proponent of the Monolith First approach, I've found Kamil Grzybek's Modular Monolith article series quite insightful:
- Part 1: Modular Monolith: A Primer
- Part 2: Modular Monolith: Architectural Drivers
Starting with a well designed and internally modularised monolith does seem to be the way to go. When architectural drivers change, like different modules evolving at different rates, it comes natural to split up the monolith into a few modules that are life-cycled and deployed independently, essentially creating micro-services. Scalability concerns in a module is another example of an architectural driver that often leads to such a split up.
You've got several tools at your disposal to keep your monolith properly modularised.
- Designing proper module interfaces used by other parts of the system to interact with the module.
- Use of Java packages to encapsulate modules, while keeping an eye on cyclomatic complexity.
- The Java 9 module system. (Although I haven't used this myself yet.)
- Smart use of multi-module builds in Maven. If you get this right, extracting a module to become its own micro-service might be a relatively trivial matter.
Thursday, April 7, 2011
Software and Mud
There seems to be a strange relationship between software and mud. Many will be familiar with the Big Ball of Mud architectural style and there are countless examples of code that resembles mud more than anything else.
A while ago a colleague of mine used an analogy between a software related problem he was facing and wading through mud (or as we say in Dutch: "door de moose ploegen"). I'm amazed at how poignant this analogy actually is. Think of a situation where the first thing you notice is that you have mud on the soles of your shoes. As you trot on you quickly end up knee deep in mud. At this point you can either back out and retreat to solid ground, or persevere, in which case you often end up stuck!

Of course you might also make it through, as John Carmack once noted:
Many software related situations come to mind that closely follow this story:
(As an aside: some people can wade through mud more elegantly than others, as Kate Moss illustrates.)
A while ago a colleague of mine used an analogy between a software related problem he was facing and wading through mud (or as we say in Dutch: "door de moose ploegen"). I'm amazed at how poignant this analogy actually is. Think of a situation where the first thing you notice is that you have mud on the soles of your shoes. As you trot on you quickly end up knee deep in mud. At this point you can either back out and retreat to solid ground, or persevere, in which case you often end up stuck!

Of course you might also make it through, as John Carmack once noted:
Yes, you can make windows do anything you want to if you have enough time to beat on it, but you can come out of it feeling like you just walked through a sewer. [.plan]
Many software related situations come to mind that closely follow this story:
- Merging exercises gone wrong
- Refactoring plans that start simple but spiral out of control
- Anything to do with character encoding
- ...
(As an aside: some people can wade through mud more elegantly than others, as Kate Moss illustrates.)
Sunday, December 13, 2009
Concurrency Hides Latency
To paraphrase Brian Goetz's excellent The Concurrency Revolution: The Hardware Story talk at Devoxx09: "concurrency hides latency". In other words: use concurrency to combat latency. This really seems to be the common theme in current-day software development.
In his talk, Brian explained how CPU speeds have increased at a much faster pace than memory speeds the last few decades. This has resulted in a situation where going to main memory is now extremely expensive in terms of CPU time. It could take several hundred clock cycles to fetch some data from main memory. Brian captured the problem in anther very quotable statement: "memory is the new disk". Hardware designers have been trying to minimize the impact of memory latency on computer performance by clever tricks like speculative execution and extensive caching. However, we've now come to a point where these techniques are breaking down because the gap between CPU and memory speed is just too big. As a result we're seeing more and more multi-core CPUs: slower in absolute terms but designed for concurrency so latency is less of a problem.
In web applications, a round-trip to the server involves a lot of latency, so web applications are doing more things on the client in JavaScript and hide this latency by concurrently doing other things: think AJAX. In a typical back-end business processing system, a round-trip to the database also involves a lot of latency, so we use techniques like event driven architectures to process several transactions concurrently, again hiding the latency cost.
More and more performance is about data, not code (another quote from Brian's talk)! I've seen several situations where the efficiency of a particular piece of software is completely determined by it's data access strategy. Relatively speaking, fetching data from the database is so expensive that it doesn't matter that you process that data in a naive or unoptimized way. All of this of course also implies that a key design question is the data access patterns and data structures used by the application. If you want a fast application, data is your key concern, and you can fight the latency of getting to the data by using concurrency.
In his talk, Brian explained how CPU speeds have increased at a much faster pace than memory speeds the last few decades. This has resulted in a situation where going to main memory is now extremely expensive in terms of CPU time. It could take several hundred clock cycles to fetch some data from main memory. Brian captured the problem in anther very quotable statement: "memory is the new disk". Hardware designers have been trying to minimize the impact of memory latency on computer performance by clever tricks like speculative execution and extensive caching. However, we've now come to a point where these techniques are breaking down because the gap between CPU and memory speed is just too big. As a result we're seeing more and more multi-core CPUs: slower in absolute terms but designed for concurrency so latency is less of a problem.
In web applications, a round-trip to the server involves a lot of latency, so web applications are doing more things on the client in JavaScript and hide this latency by concurrently doing other things: think AJAX. In a typical back-end business processing system, a round-trip to the database also involves a lot of latency, so we use techniques like event driven architectures to process several transactions concurrently, again hiding the latency cost.
More and more performance is about data, not code (another quote from Brian's talk)! I've seen several situations where the efficiency of a particular piece of software is completely determined by it's data access strategy. Relatively speaking, fetching data from the database is so expensive that it doesn't matter that you process that data in a naive or unoptimized way. All of this of course also implies that a key design question is the data access patterns and data structures used by the application. If you want a fast application, data is your key concern, and you can fight the latency of getting to the data by using concurrency.
Monday, November 2, 2009
Event Driven Architecture for Systems that Never Stop
InfoQ posted an excellent talk by Joe Armstrong (of Erlang fame) titled Systems that Never Stop (and Erlang). Joe presents 6 laws that he sees as crucial to systems that never stop (i.e. systems with X nines of reliability): Isolation, Concurrency, Failure Detection, Fault Identification, Live Code Upgrade, and Stable Storage.
The Erlang language and runtime were designed for exactly this kind of environment so it comes as no surpise that it shines in all areas. However, it occurred to me that an Event Driven Architecture goes a long way in building a system that never stops with more mainstream tools like Java and a relational database.
So in all, an event driver architecture goes a long way in bringing you a system that never stops. This of course in stark contrast with batch systems, that have a hard time living up to a number of these laws.
The Erlang language and runtime were designed for exactly this kind of environment so it comes as no surpise that it shines in all areas. However, it occurred to me that an Event Driven Architecture goes a long way in building a system that never stops with more mainstream tools like Java and a relational database.
- Isolation: Crashing of one process should not influence any other process, effectively isolating different processes. The big boon here is that this can really improve the reliability of your system: the probability of two independent processes failing is only half that of a single process failing.
In an event driven world events are typically handled independently of one-another. Failure to process a particular event does not affect the processing of any other event in the system. - Concurrency: A runtime environment should not put any undue constraints on how far you can take concurrency, i.e. the number of threads or processes in your system. Designing a system with concurrency in mind allows you to take advantage of current day hardware (multi-cores and the like), but also goes hand in hand with the first law: independent, isolated processes can run concurrently without much problem.
In an event driver system, multiple independent event processors can run concurrently on a single machine, taking advantage of all CPU cores available on the machine. Furthermore, event driven systems scale horizontally, allowing you to easily add additional machines with even more event processors. - Failure Detection: You must be able to detect when things go wrong, for instance by having a nanny like process that can monitor the system and can take appropriate action when failures occur. Furthermore, you should fail early, limiting the potential impact of the failure.
In an event driven system you typically have failover handling that holds on to events that failed processing for later analysis or retry. One system property that Joe does not mention in his talk but is very relevant in this area is idempotence: being able to retry things without affecting the final result. With some effort, idempotence comes naturally in an event driven system. - Fault Identification: If something has gone wrong, and you've detected it as per the previous law, you need to be able to identify what went wrong.
Again, in event driven systems this comes naturally: the event that failed processing is normally maintained by the failover system, along with error messages and the like, and can easily be used to simulate what happened. - Life Code Upgrade: You shouldn't need to bring down your system for an upgrade. Whether or not this is important to you is of course very dependent of the application that you're writing. Still, if you have a cluster of event processors, you can upgrade them one by one, keeping the system live at all time. On a single machine you could use things like OSGi to attain similar results.
- Stable Storage: This essentially boils down to having transactional (ACID) storage available. In your average enterprise application this storage will be provided by an RDBMS (Oracle, ...). Most current-day MOM solutions (MQ, ...) are also completely transactional, and even XA aware. So there should be no need to ever loose any information in an event driven system build on top of these technologies.
So in all, an event driver architecture goes a long way in bringing you a system that never stops. This of course in stark contrast with batch systems, that have a hard time living up to a number of these laws.
Subscribe to:
Posts (Atom)
