during: 2024
Exploring Generative AI
Generative AI and particularly LLMs (Large Language Models) have exploded into the public consciousness. Like many software developers Birgitta is intrigued by the possibilities, but unsure what exactly it will mean for our profession in the long run. She has taken on a role in Thoughtworks to coordinate our work on how this technology will affect software delivery practices. On this page she posts a series of memos to describe what she and our colleagues are learning and thinking.
Using the Strangler Fig with Mobile Apps
Incremental replacement of a legacy mobile application is a challenging concept to articulate and execute. However, we believe by making the investment in the pre-requisites of legacy modernization, it is posible to yield benefits in the long term. This article explores the Strangler Fig pattern and how it can be applied to mobile applications. We chart the journey of an enterprise who refused to accept the high cost and risk associated with a full rewrite of their mobile application. By incrementally developing their new mobile app alongside a modular architecture, they were able to achieve significant uplifts in their delivery metrics.
Legacy Modernization meets GenAI
So far, most attention to Generative Artificial Intelligence (GenAI) in software development is on generating code. But we believe there is as much, if not more, value in understanding existing code - particularly long-lived, large, and complex legacy systems. We have been experimenting with GenAI for modernization with our clients, embodied in a tool called CodeConcise, which combines a Large Language Model (LLM) with a knowledge graph derived from the abstract syntax trees of a codebase. We have seen positive results from this approach in both drawing out low-level requirements and building a high-level explanation of a system.
Governing data products using fitness functions
Decentralized data management requires automation to scale governance effectively. Fitness functions are a powerful automated governance technique we've applied to data products within the context of a Data Mesh. Since data products serve as the foundational building blocks (architectural quanta) of a data mesh, ensuring robust governance around them significantly increases the chances of a successful data mesh transformation. In this article, we’ll explore how to implement this technique. We'll start by designing simple tests to assess key architectural characteristics of a data product, and then we'll explore how to automate their execution by leveraging metadata about the data products.
Cycle Time
Cycle Time is a measure of how long it takes to get a new feature in a software system from idea to running in production. In Agile circles, we try to minimize cycle time. We do this by defining and implementing very small features and minimizing delays in the development process. Although the rough notion of cycle time, and the importance of reducing it, is common, there is a lot of variations on how cycle time is measured.
Strangler Fig
During a vacation in the rain forests of Queensland in 2001, we saw some strangler figs. These are vines that germinate in a nook of a tree. As it grows, it draws nutrients from the host tree until it reaches the ground to grow roots and the canopy to get sunlight. It can then become self-sustaining, and its original host tree may die leaving the fig as an echo of its shape. This gradual process of replacing the host tree struck me as a striking analogy to the way I saw colleagues doing modernization of legacy software systems. A couple of years later I posted a brief blog post about this metaphor. While I've not used the term in my writing since then, it caught attention anyway, and the term “Strangler Fig” is now often used to describe a gradual approach to legacy modernization.
Instead of restricting AI and algorithms, make them explainable
The steady increase in deployment of AI tools has led a lot of people concerned about how software makes decisions that affect our lives. In one example, its about “algorithmic” feeds in social media that promote posts that drive engagement. A more serious impact can come from business decisions, such as how much premium to charge in car insurance. This can extend to affecting legal decisions, such as suggesting sentencing guidelines to judges.
Test-Driving HTML Templates
When building a server-side rendered web application, it is valuable to test the HTML that's generated through templates. While these can be tested through end-to-end tests running in the browser, such tests are slow and more work to maintain than unit tests. Unit tests, written in the server-side environment, can check for valid HTML, and extract elements with CSS selectors to test the details of generated HTML. These test cases can be defined in a simple data structure to make them easier to understand and enhance. Browser testing tools are still needed to unit test the behavior of the generated HTML, together with the associated CSS and JavaScript.
Data Fetching Patterns in Single-Page Applications
When a single-page application needs to fetch data from a remote source, it needs to do so while remaining responsive and providing feedback to the user during an often slow query. Five patterns help with this. Asynchronous State Handler wraps these queries with meta-queries for the state of the query. Parallel Data Fetching minimizes wait time. Fallback Markup specifies fallback displays in markup. Code Splitting loads only code that's needed. Prefetching gathers data before it may needed to reduce latency when it is.
Using Footnotes
How I use, and render, footnotes
Uncovering the Seams in Mainframes for Incremental Modernisation
Mainframe systems continue to run much of the world's computing workload, but it's often difficult to add new features to support growing business needs. Furthermore the architectural challenges that make them slow to enhance also make them hard to replace. To reduce the risk involved, we should use an incremental approach to legacy displacement, gradually replacing legacy capabilities with implementations in modern technology. This strategy requires us to introduce seams into the mainframe system: points in which we could divert logic flow into newer services. In a recent project we investigated several approaches to introduce these seams into a long-lived mainframe system.
Farewell John Kordyback
John Kordyback, a treasured colleague and friend, died last week, aged 64.
2024 Analysis of Traffic to martinfowler.com
Although I do keep an eye on traffic to the site, especially with newly published articles, it's been a while since I've done a proper overview of the traffic to martinfowler.com. (The previous ones were in 2014 and 2018.) So in between my editing work, I've spent some time looking through the traffic data. Currently this page summarizes what I've understood so far, but I'll be adding more sections as I probe further.
Measuring Developer Productivity via Humans
Measuring developer productivity is a difficult challenge. Conventional metrics focused on development cycle time and throughput are limited, and there aren't obvious answers for where else to turn. Qualitative metrics offer a powerful way to measure and understand developer productivity using data derived from developers themselves. Organizations should prioritize measuring developer productivity using data from humans, rather than data from systems.
What if we rotate pairs every day?
Benefits of pair programming are widely accepted but advice around pair rotation remains controversial. When and how frequently should teammates rotate pairs? And… What if we rotate pairs every day? We worked with three teams through an exercise of daily pair rotation. We developed a lightweight methodology to help teams reflect on the benefits and challenges of pairing and how to solve them. Initial fears were overcome and teams discovered the benefits of frequently rotating pairs. We learned that pair swapping frequently greatly enhances the benefits of pairing. Here we share the methodology we developed, our observations, and some common fears and insight shared by the participating team members.
Patterns of Legacy Displacement
When faced with the need to replace existing software systems, organizations often fall into a cycle of half-completed technology replacements. Our experiences have taught us a series of patterns that allow us to break this cycle, relying on: a deliberate recognition of the desired outcomes of displacing the legacy software, breaking this displacement in parts, incrementally delivering these parts, and changing the culture of the organization to recognize that change is the unvarying reality.
Periodic Face-to-Face
Improvements in communications technology have led an increasing number of teams that work in a Remote-First style, a trend that was boosted by the forced isolation of Covid-19 pandemic. But a team that operates remotely still benefits from face-to-face gatherings, and should do them every few months.
Engineering Practices for LLM Application Development
LLM engineering involves much more than just prompt design or prompt engineering. In this article, we share a set of engineering practices that helped us deliver a prototype LLM application rapidly and reliably in a recent project. We'll share techniques for automated testing and adversarial testing of LLM applications, refactoring, as well as considerations for architecting LLM applications and responsible AI.
Continuous Integration
Continuous Integration is a software development practice where each member of a team merges their changes into a codebase together with their colleagues changes at least daily. Each of these integrations is verified by an automated build (including test) to detect integration errors as quickly as possible. Teams find that this approach reduces the risk of delivery delays, reduces the effort of integration, and enables practices that foster a healthy codebase for rapid enhancement with new features.
Legacy Seam
When working with a legacy system it is valuable to identify and create seams: places where we can alter the behavior of the system without editing source code. Once we've found a seam, we can use it to break dependencies to simplify testing, insert probes to gain observability, and redirect program flow to new modules as part of legacy displacement.
My favorite musical discoveries of 2023
Six of my favorite new music acquisitions in 2024