https://martinfowler.com/feed.atomMartin FowlerMaster feed of news and updates from martinfowler.comMartin Fowlermartin@martinfowler.comhttps://martinfowler.com2024-03-15T11:04:00-04:00Code samples for the opening chapter of Refactoring2024-03-15T11:04:00-04:00tag:martinfowler.com,2024-03-15:Code-samples-for-the-opening-chapter-of-Refactoring
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/2024-refactoring-code-samples.html'><img src = 'https://martinfowler.com/articles/happy-coding.png' width = ''></img></a></div>
<p>From time to time people ask me for a copy of the code I used in the
opening chapter of <a href = '/books/refactoring.html'>Refactoring</a>, so
they can follow along themselves. I had Reasons for not providing this code,
specifically laziness. Fortunately Emily Bache is more dedicated, and she
has set up a github repository - the <a href = 'https://github.com/emilybache/Theatrical-Players-Refactoring-Kata'>Theatrical
Players Refactoring Kata</a> - with the code, and enough tests to make it
reasonable to do the refactoring.</p>
<p>The repository goes further than this, however, in that it includes similar
sample code in a dozen languages, including C, Java, Rust, and Python.</p>
<p>She has recently posted a <a href = 'https://www.youtube.com/watch?v=TjIrKEaOiVw'>video to her YouTube
channel</a>, which outlines why she encourages folks to use this code while
they are reading that chapter. Her channel includes a lot of videos on good
code technique, and she has a <a href = 'https://www.patreon.com/EmilyBache'>Patreon</a> for readers to support
her work.</p>The Benefits of Qualitative Metrics2024-03-13T10:36:00-04:00tag:martinfowler.com,2024-03-13:The-Benefits-of-Qualitative-Metrics
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html#TheTwoTypesOfQualitativeMetrics'><img src = 'https://martinfowler.com/articles/measuring-developer-productivity-humans/metaimage.png' width = ''></img></a></div>
<p><b class = 'author'>Abi Noda</b> and <b class = 'author'>Tim Cochran</b> continue their
discussion on using qualitative metrics to assess the productivity of
development teams. <a href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html#TheTwoTypesOfQualitativeMetrics'>In this installment</a> they classify qualitative metrics into
attitudinal and behavioral metrics. We also see that qualitative metrics
allow you to measure things that are otherwise unmeasurable, provide missing
visibility, and supply necessary context for quantitative data.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html#TheTwoTypesOfQualitativeMetrics'>more…</a></p>Measuring Developer Productivity via Humans2024-03-12T09:36:00-04:00tag:martinfowler.com,2024-03-12:Measuring-Developer-Productivity-via-Humans
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html'><img src = 'https://martinfowler.com/articles/measuring-developer-productivity-humans/metaimage.png' width = ''></img></a></div>
<p>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.
<b class = 'author'>Abi Noda</b> and <b class = 'author'>Tim Cochran</b> begin their
discussion by <a href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html'>explaining what a qualitative metric is </a>and
why we shouldn't reject them for being subjective or unreliable. </p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/measuring-developer-productivity-humans.html'>more…</a></p>What if we rotate pairs every day?2024-03-06T10:33:00-05:00tag:martinfowler.com,2024-03-06:What-if-we-rotate-pairs-every-day-
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/rotate-pairs-experiment.html'><img src = 'https://martinfowler.com/articles/rotate-pairs-experiment/metaimage.png' width = ''></img></a></div>
<p>When pair programming, it's important to rotate the pairs frequently,
but many organizations that do pair programming are reluctant to do that.
<b class = 'author'>Gabriel Robaina</b> and <b class = 'author'>Kieran Murphy</b> ask
the question: “What if we rotate pairs every day?” and worked with three
teams through an exercise of daily pair rotation. They 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. They
learned that pair swapping frequently greatly enhances the benefits of
pairing. <a href = 'https://martinfowler.com/articles/rotate-pairs-experiment.html'>Their article shares</a> the methodology they
developed, their observations, and some common fears and insights shared
by the participating team members.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/rotate-pairs-experiment.html'>more…</a></p>Patterns of Legacy Displacement: Event Interception2024-03-05T10:03:00-05:00tag:martinfowler.com,2024-03-05:Patterns-of-Legacy-Displacement--Event-Interception
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/patterns-legacy-displacement/event-interception.html'><img src = 'https://martinfowler.com/articles/patterns-legacy-displacement/card.png' width = ''></img></a></div>
<p>When we gradually replace a legacy system, we have plenty of cases
where the legacy system and its replacement need to interact. Since these
legacy systems are often difficult, and costly, to change, we need a
mechanism that can integrate elements of the replacement while minimizing
the impact to the legacy system. <b class = 'author'>Ian Cartwright, Rob Horn, and
James Lewis</b> explain how we can use <a href = 'https://martinfowler.com/articles/patterns-legacy-displacement/event-interception.html'>Event Interception</a> on
state-changing events, allowing us to forward them to the
replacement.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/patterns-legacy-displacement/event-interception.html'>more…</a></p>Bliki: Periodic Face-to-Face2024-02-27T09:17:00-05:00https://martinfowler.com/bliki/PeriodicFaceToFace.html
<p>Improvements in communications technology have led an increasing number of
teams that work in a <a href="/articles/remote-or-co-located.html#remote-first">Remote-First</a>
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.</p>
<p>Remote-first teams have everyone in a separate location, communicating
entirely by email, chat, video and other communication tools. It has definite
benefits: people can be recruited to the team from all over the world, and we can
involve people with care-giving responsibilities. Wasteful hours of
frustrating commutes can be turned into productive or recuperative time. </p>
<p>But however capable folks may be at remote working, and however nifty modern
collaboration tools become, there is still nothing like being in the same
place with the other members of a team. Human interactions are always richer
when they are face-to-face. Video calls too easily become transactional, with
little time for the chitchat that builds a proper human relationship. Without
those deeper bonds, misunderstandings fester into serious relationship
difficulties, and teams can get tangled in situations that would be
effectively resolved if everyone were able to talk in person.</p>
<p>A regular pattern I see from those who are effective in remote-first work
is that they ensure regular face-to-face meetings. During these they schedule
those elements of work that are done better together. Remote work
is more effective for tasks that require solo concentration, and modern tools
can make remote pairing workable. But tasks that require lots of input from
many people with rapid feedback are much easier to do when everyone is in the
same room. No video-conference system can create the that depth of
interaction, staring at a computer screen to see what other people are doing
is draining, with no opportunity to pop out for a coffee together to break up the
work. Debates about product strategy, explorations of systems architecture,
explorations of new ground - these are common tasks for when the team is
assembled.</p>
<p>For people to work effectively together they need to trust each other,
aware of how much they can rely on each other. Trust is hard to develop
online, where there isn't the social cues that can happen when we are in the
same room. Thus <b>the most valuable part of a face-to-face gathering isn't
the scheduled work</b>, it's chitchat while getting a coffee, and conviviality
over lunch. Informal conversations, mostly not about work, forge the human
contact that makes the work interactions be more effective.</p>
<p>Those guidelines suggest what the content for a face-to-face should be.
Working together is both valuable in its own right, and an important part of
team bonding. So we should set a full day of work, focusing on those tasks
that benefit from the low-latency communication that comes from being
together. We should then include what feels like too much time for
breaks, informal chatter, and opportunities to step outside the office. I
would avoid any artificial “team building” exercises, if only because of how
much I hate them. Those who do gatherings like this stress the value from
everyone energized afterwards, and thus able to be more effective in the
following weeks.</p>
<p>Remote teams can be formed at large distances, and it's common to see
members separated by hours of travel. For such teams, the rule of thumb I would
use is to get together for a week every two or three months. After the team
has become seasoned they may then decide to reduce the frequency, but I would
worry if a team isn't having at least two face-to-face meetings a year. If a
team is all in the same city, but using a remote-first style to reduce
commuting, then they can organize shorter gatherings, and do them more
frequently.</p>
<p>This kind of gathering may lead to rethinking of how to configure office
space. Much has been made of how offices are far less used since the pandemic.
Offices could well become less of a day-to-day workspace, and more a location
for these kinds of irregular team gatherings. This leads to a need for
flexible and comfortable team gathering spaces.</p>
<p>Some organizations may balk at the costs of travel and accommodation for a
team assembly like this, but they should think of it as an investment in the
team's effectiveness. Neglecting these face-to-faces leads to teams getting
stuck, heading off in the wrong direction, plagued with conflict, and people
losing motivation. Compared to this, saving on airplanes and hotels is a false
economy.</p>
<div class="furtherReading">
<h2>Further Reading</h2>
<p>Remote-first is one form of remote work, I explore the different styles
of remote working and their trade-offs in <a href="/articles/remote-or-co-located.html">Remote versus Co-located
Work</a>. </p>
<p>At Thoughtworks, we learned the importance of regular face-to-face
gatherings for remote teams when we first started our offshore development
centers nearly two decades ago. These generated the practices I describe in <a href="/articles/agileOffshore.html">Using an Agile Software Process with
Offshore Development</a>. </p>
<p>Remote work, particularly when crossing time zones, puts a greater
premium on asynchronous patterns of collaboration. My colleague Sumeet
Moghe, a product manager, goes into depth on how to do this in his book
<a href="https://www.amazon.com/gp/product/0138187533/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0138187533&linkCode=as2&tag=martinfowlerc-20">The Async-First Playbook</a></p>
<p>Atlassian, a software product company, has recently entirely shifted to
remote working, and published a <a href="https://atlassianblog.wpengine.com/wp-content/uploads/2024/01/lessonslearned.pdf">report on its
experiences</a>. They have learned that it's wise for teams to have a
face-to-face gathering roughly three times per year. Claire Lew <a href="https://signalvnoise.com/svn3/how-to-build-social-connection-in-a-remote-team/#comments">surveyed remote-first teams in 2018</a>, noting that a quarter
of their respondents did retreats “several times a year”. 37Signals has
operated as a remote-first company for nearly two decades and <a href="https://world.hey.com/dhh/recharging-trust-batteries-with-meetups-in-a-remote-company-5ad0cbeb">schedules meetups twice a year</a>.</p>
</div>
<div class="acknowledgements">
<h2>Acknowledgements</h2>
<p>Alejandro Batanero, Andrew Thal, Chris Ford, Heiko Gerin, Kief Morris, Kuldeep Singh, Matt Newman, Michael Chaffee, Naval
Prabhakar, Rafael Detoni, and Ramki Sitaraman discussed drafts of
this post on our internal mailing list. </p>
</div>
Engineering Practices for LLM Application Development2024-02-13T12:22:00-05:00tag:martinfowler.com,2024-02-13:Engineering-Practices-for-LLM-Application-Development
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/engineering-practices-llm.html'><img src = 'https://martinfowler.com/articles/engineering-practices-llm/refactor-me-monster-comic.png' width = ''></img></a></div>
<p>LLM engineering involves much more than just prompt design or prompt
engineering. Here <b class = 'author'>David Tan</b> and <b class = 'author'>Jessie
Wang</b> reflect on how regular engineering practices such as
testing and refactoring <a href = 'https://martinfowler.com/articles/engineering-practices-llm.html'>helped them deliver</a> a prototype LLM
application rapidly and reliably.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/engineering-practices-llm.html'>more…</a></p>Onboarding bottleneck: final installment2024-01-31T11:57:00-05:00tag:martinfowler.com,2024-01-31:Onboarding-bottleneck--final-installment
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#PairProgrammingAsACriticalOnboardingTechnique'><img src = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/card.png' width = ''></img></a></div>
<p><b class = 'author'>Tim and Prem</b> finish their article on effective
onboarding. <a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#PairProgrammingAsACriticalOnboardingTechnique'>They discuss</a> the value of pair programming, setting up
personal environments, and removing friction from the process.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#PairProgrammingAsACriticalOnboardingTechnique'>more…</a></p>Onboarding bottleneck: more steps for good onboarding2024-01-30T09:33:00-05:00tag:martinfowler.com,2024-01-30:Onboarding-bottleneck--more-steps-for-good-onboarding
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#IncludeNewHiresInTheCompanyCulture'><img src = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/card.png' width = ''></img></a></div>
<p><b class = 'author'>Tim and Prem</b> continue outlining the steps for an
effective onboarding process. <a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#IncludeNewHiresInTheCompanyCulture'>They talk about</a> including new hires in the
company culture, nailing the post-offer and first-day experience, and
investing in self-service knowledge management</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#IncludeNewHiresInTheCompanyCulture'>more…</a></p>Improving my Emacs experience with completion2024-01-25T13:20:00-05:00https://martinfowler.com/articles/2024-emacs-completion.htmlMartin Fowlermartin@martinfowler.comhttps://martinfowler.com<p>I’ve been using Emacs for many years, using it for any writing for my website, writing my books, and most of my programming. (Exceptions have been IntellJ IDEA for Java and RStudio for R.) As such I’ve been happy to see a lot of activity in the last few years to improve Emacs’s capabilities, making it feel rather less than a evolutionary dead end. One of the biggest improvements to my Emacs experience is using regexs for completion lists.</p>
<p>Many Emacs commands generate lists of things to pick from. I want to visit (open) a file I type the key combination to find a file, and Emacs pops up a list of candidate files in the minibuffer (a special area for to interact with commands). These file lists can be quite long, particularly should I ask for a list of all files in my current project.</p>
<p>To specify the file I want, I can type some text to filter the list, so if I want to open the file <code>articles/simple/2024-emacs-completion.md</code> I might type <code>emacs</code>. I don’t have to get only that one file, just filtering to a small enough list is often enough.</p>
<p>There’s a particular style of regex builder that I find the most helpful, one that separates regexs by spaces. This would allow me to type <code>articles emacs</code> to get a list of any file paths that contain “articles” and “emacs” in their file path. It essentially turns the string “articles emacs” into the regex <code>\\(articles\\).*\\(emacs\\)</code>. Better yet, such a matcher allows me to type the regexs in any order, so that “emacs articles” would also match. This way once the first regex pops up a filtered list, I can use a second regex to pick the one I want, even if the distinguishing regex is earlier than my initial search.</p>
<p>Installing such a completion matcher has had a remarkable effect on my use of Emacs, since it makes it a breeze to filter large lists when interacting with commands. One of the most significant of these is how it changes my use of <code>M-x</code>, the key combo that brings up a list of all interactive Emacs functions. With a regex matcher to filter the list, it allows me to invoke an Emacs command using its name, with just a few keystrokes. That way I don’t have to remember the keyboard shortcut. With this, I invoke commands that I use less frequently through <code>M-x</code>. I don’t list all open buffers very often, so rather than try to remember the key combination for it, I just type <code>M-x ib</code> and <code>ibuffer</code> quickly pops up. This is helped that the command I use for <code>M-x</code> (<code>counsel-M-x</code>) inserts a “<code>^</code>” as the first character in the regex, which anchors the first regex to the beginning of the line. Since I prefix all my self-written functions with <code>mf-</code>, I can easily find my own functions, even if they have a long name. I wrote a command to remove the domain from a URL, I call it <code>mf-url-remove-domain</code> and can invoke it with <code>M-x mf url</code>.</p>
<p>There are quite a few packages in Emacs that do this kind of matching, enough to be rather confusing. The one I’m using these days is <a href="https://oremacs.com/swiper/">Ivy</a>. By default it uses a space-separated regex matcher, but one that doesn’t support any order. To configure it the way I like it I use</p>
<pre><code>(setq ivy-re-builders-alist '((t . ivy--regex-ignore-order)))
</code></pre>
<p>Ivy is part of a package called <code>counsel</code> that includes various commands that enhance these kind of selections.</p>
<p>Ivy isn’t the only tool that does this kind of thing. Indeed the world of completion tools in Emacs is one I find very confusing: lots of tools with overlaps and interactions that I don’t really understand. The tools in this territory include <a href="https://emacs-helm.github.io/helm/">Helm</a>, <a href="https://company-mode.github.io/">company</a>, <a href="https://github.com/minad/vertico">Vertico</a>, and <a href="https://github.com/minad/consult">Consult</a>. Mastering Emacs has an article on <a href="https://www.masteringemacs.org/article/understanding-minibuffer-completion">Understanding Minibuffer Completion</a>, but it doesn’t explain how the mechanisms it talks about fit in with what Ivy does, and I haven’t spent the time to figure it all out.</p>
<p>And as a general note, I strongly recommend the book <a href="https://www.masteringemacs.org/">Mastering Emacs</a> to learn how to use this incredible tool. Emacs has so many capabilities, that even a decades-old user like me found that book led to “I didn’t know it could do that” moments.</p>
<p>For those that are curious, here’s the relevant bits of my Emacs config</p>
<pre><code>(use-package ivy
:demand t
:diminish ivy-mode
:config
(ivy-mode 1)
(counsel-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-use-selectable-prompt t)
(setq ivy-ignore-buffers '(\\` " "\\`\\*magit"))
(setq ivy-re-builders-alist '(
(t . ivy--regex-ignore-order)
))
(setq ivy-height 10)
(setq counsel-find-file-at-point t)
(setq ivy-count-format "(%d/%d) "))
(use-package counsel
:bind (
("C-x C-b" . ivy-switch-buffer)
("C-x b" . ivy-switch-buffer)
("M-r" . counsel-ag)
("C-x C-d" . counsel-dired)
("C-x d" . counsel-dired)
)
:diminish
:config
(global-set-key [remap org-set-tags-command] #'counsel-org-tag))
(use-package swiper
:bind(("M-C-s" . swiper)))
(use-package ivy-hydra)
</code></pre>
Onboarding bottleneck: creating a path to effectiveness2024-01-24T10:45:00-05:00tag:martinfowler.com,2024-01-24:Onboarding-bottleneck--creating-a-path-to-effectiveness
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#exit'><img src = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/card.png' width = ''></img></a></div>
<p><b class = 'author'>Tim and Prem</b> begin their discussion of how to get out of the
difficulties of onboarding by explaining how to <a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#exit'>create a path to
effectiveness</a> for new hires. Such a path outlines the needs of employee
and how the onboarding process should fulfill them.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html#exit'>more…</a></p>Bottlenecks of Scaleups #06: Onboarding2024-01-23T09:13:00-05:00tag:martinfowler.com,2024-01-23:Bottlenecks-of--Scaleups--06--Onboarding
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html'><img src = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/card.png' width = ''></img></a></div>
<p>The last year has been a hard one for the technology industry, which
faced its greatest waves of cutbacks and layoffs since the dotcom crash at
the beginning of the century. As 2024 begins, we're seeing the early signs of a
turn-around, which hopefully means technology organizations will soon be thinking of
hiring again. Should such happy days return, firms will again run into
the common problem of
taking too long for new hires to become effective. <b class = 'author'>Tim
Cochran and Premanand Chandrasekaran</b> address this in the sixth part of our
series on the bottlenecks of scaleups. In this first installment, Tim and Prem
look the signs that a growing organization is <a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html'>running into this bottleneck</a>.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/06-onboarding.html'>more…</a></p>A major revision of Continuous Integration2024-01-18T10:01:00-05:00tag:martinfowler.com,2024-01-18:A-major-revision-of-Continuous-Integration
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/continuousIntegration.html'><img src = 'https://martinfowler.com/articles/continuousIntegration/card.png' width = ''></img></a></div>
<p>At the turn of the century, I was lucky to involved in several
projects that developed the practice of Continuous Integration. I wrote
up our lessons from this work in article on my website, where it
continues to be a oft-referenced resource for this important practice.
Late last year a colleague contacted me to say that the article, now
nearly twenty years old, was still useful, but was showing its age. He
sent me some suggested revisions, and I used this as a trigger to do <a href = 'https://martinfowler.com/articles/continuousIntegration.html'>a
thorough revision of the article</a>, considering every section in the
original, and adding new ones to deal with issues that have appeared in the last
two decades.</p>
<p>During those decades Feature Branching has been widely adopted in the
industry. Many colleagues of mine feel that Continuous Integration is a
better fit for many teams, I hope this article will help readers assess
if this is the case, and if so, how to implement Continuous Integration
effectively.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/continuousIntegration.html'>more…</a></p>Bliki: Legacy Seam2024-01-04T09:13:00-05:00https://martinfowler.com/bliki/LegacySeam.html
<p>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.</p>
<p>Michael Feathers coined the term “seam” in the context of legacy systems in
his book <a href="https://www.amazon.com/gp/product/0131177052/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0131177052&linkCode=as2&tag=martinfowlerc-20">Working Effectively with Legacy Code</a>. His definition: <b>“a seam is a place
where you can alter behavior in your program without editing in that place”</b>.</p>
<p>Here's an example of where a seam would be handy. Imagine some code to
calculate the price of an order.</p>
<pre>// TypeScript
export async function calculatePrice(order:Order) {
const itemPrices = order.items.map(i => calculateItemPrice(i))
const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
const discount = calculateDiscount(order)
const shipping = await <span class="highlight">calculateShipping</span>(order)
const adjustedShipping = applyShippingDiscounts(order, shipping)
return basePrice + discount + adjustedShipping
}
</pre>
<p>The function <code>calculateShipping</code> hits an external service, which is slow (and
expensive), so we don't want to hit it when testing. Instead we want to
introduce a <a href="/bliki/TestDouble.html">stub</a>, so we can provide a canned and deterministic response for
each of the testing scenarios. Different tests may need different responses
from the function, but we can't edit the code of
<code>calculatePrice</code> inside the test. Thus we need to introduce a seam around the call
to <code>calculateShipping</code>, something that will allow our test to
redirect the call to the stub.</p>
<p>One way to do this is to pass the function for
<code>calculateShipping</code> as a parameter</p>
<pre>export async function calculatePrice(order:Order, <span class="highlight">shippingFn</span>: (o:Order) => Promise<number>) {
const itemPrices = order.items.map(i => calculateItemPrice(i))
const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
const discount = calculateDiscount(order)
const shipping = await <span class="highlight">shippingFn</span>(order)
const adjustedShipping = applyShippingDiscounts(order, shipping)
return basePrice + discount + adjustedShipping
}
</pre>
<p>A unit test for this function can then substitute a simple stub.</p>
<pre>const shippingFn = async (o:Order) => 113
expect(await calculatePrice(sampleOrder, shippingFn)).toStrictEqual(153)</pre>
<p>Each seam comes with an <b>enabling point</b>: “a place where you can
make the decision to use one behavior or another” <a href="https://www.amazon.com/gp/product/0131177052/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0131177052&linkCode=as2&tag=martinfowlerc-20">[WELC]</a>. Passing the function as
parameter opens up an enabling point in the caller of
<code>calculateShipping</code>.</p>
<p>This now makes testing a lot easier, we can put in different values of
shipping costs, and check that <code>applyShippingDiscounts</code> responds
correctly. Although we had to change the original source code to introduce the
seam, any further changes to that function don't require us to alter that
code, the changes all occur in the enabling point, which lies in the test code.</p>
<p>Passing a function as a parameter isn't the only way we can introduce a
seam. After all, changing the signature of <code>calculateShipping</code> may
be fraught, and we may not want to thread the shipping function parameter
through the legacy call stack in the production code. In this case a lookup
may be a better approach, such as using a service locator.</p>
<pre>export async function calculatePrice(order:Order) {
const itemPrices = order.items.map(i => calculateItemPrice(i))
const basePrice = itemPrices.reduce((acc, i) => acc + i.price, 0)
const discount = calculateDiscount(order)
const shipping = await <span class="highlight">ShippingServices.calculateShipping</span>(order)
const adjustedShipping = applyShippingDiscounts(order, shipping)
return basePrice + discount + adjustedShipping
}
</pre>
<pre>class ShippingServices {
static #soleInstance: ShippingServices
static init(arg?:ShippingServices) {
this.#soleInstance = arg || new ShippingServices()
}
static async calculateShipping(o:Order) {return this.#soleInstance.calculateShipping(o)}
async calculateShipping(o:Order) {return legacy_calcuateShipping(o)}
// ... more services
</pre>
<p>The locator allows us to override the behavior by defining a subclass.</p>
<pre>class ShippingServicesStub extends ShippingServices {
calculateShippingFn: typeof ShippingServices.calculateShipping =
(o) => {throw new Error("no stub provided")}
async calculateShipping(o:Order) {return this.calculateShippingFn(o)}
// more services
</pre>
<p>We can then use an enabling point in our test</p>
<pre>const stub = new ShippingServicesStub()
stub.calculateShippingFn = async (o:Order) => 113
ShippingServices.init(stub)
expect(await calculatePrice(sampleOrder)).toStrictEqual(153)</pre>
<p>This kind of service locator is a classical object-oriented way to set up a
seam via function lookup, which I'm showing here to indicate the kind of
approach I might use in other languages, but I wouldn't use this approach in
TypeScript or JavaScript. Instead I'd put something like this into a module.</p>
<pre>export let calculateShipping = legacy_calculateShipping
export function reset_calculateShipping(fn?: typeof legacy_calculateShipping) {
calculateShipping = fn || legacy_calculateShipping
}
</pre>
<p>We can then use the code in a test like this</p>
<pre>const shippingFn = async (o:Order) => 113
reset_calculateShipping(shippingFn)
expect(await calculatePrice(sampleOrder)).toStrictEqual(153)</pre>
<p>As the final example suggests, the best mechanism to use for a seam depends
very much on the language, available frameworks, and indeed the style of the
legacy system. Getting a legacy system under control means learning how to
introduce various seams into the code to provide the right kind of enabling
points while minimizing the disturbance to the legacy software. While a
function call is a simple example of introducing such seams, they can be much
more intricate in practice. A team can spend several months figuring out how
to introduce seams into a well-worn legacy system. The best mechanism for
adding seams to a legacy system may be different to what we'd do for similar
flexibility in a green field.</p>
<p>Feathers's book focuses primarily on getting a legacy system under test, as
that is often the key to being able to work with it in a sane way. But seams
have more uses than that. Once we have a seam, we are in the position to place
probes into the legacy system, allowing us to increase the observability of
the system. We might want to monitor calls to <code>calculateShipping</code>,
figuring out how often we use it, and capturing its results for separate analysis.</p>
<p>But probably the most valuable use of seams is that they
allow us to migrate behavior away from the legacy.
A seam might redirect high-value customers to a different shipping calculator.
Effective legacy displacement is founded on introducing seams into the legacy
system, and using them to gradually move behavior into a more modern environment.</p>
<p>Seams are also something to think about as we write new software, after all
every new system will become legacy sooner or later. Much of my design advice
is about building software with appropriately placed seams, so we can easily test,
observe, and enhance it. If we write our software with testing in mind, we
tend to get a good set of seams, which is a reason why <a href="/bliki/TestDrivenDevelopment.html">Test Driven Development</a> is such a useful technique.</p>
My favorite musical discoveries of 20232024-01-02T18:42:00-05:00tag:martinfowler.com,2024-01-02:My-favorite-musical-discoveries-of-2023
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/2023-music.html'><img src = 'https://martinfowler.com/articles/2023-music/card.png' width = ''></img></a></div>
<p>Another year, another time to pick <a href = 'https://martinfowler.com/articles/2023-music.html'>six favorite musical
discoveries</a>. 2023 includes ambient bluegrass, Afro-Andean funk, Northumbrian
smallpipes, dancing kora, and Ukrainian folk jazz.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/2023-music.html'>more…</a></p>Bliki: Software And Engineering2023-12-13T00:00:00-05:00https://martinfowler.com/bliki/SoftwareAndEngineering.html
<p>Throughout my career, people have compared software development to
“traditional” engineering, usually in a way to scold software developers for not
doing a proper job. As someone who got his degree in Electronic Engineering,
this resonated with me early in my career. But this way of thinking is flawed
because most people have the wrong impression of how engineering works in
practice.</p>
<p><a href="https://vanderburg.org">Glenn Vanderburg</a> has spent a lot of time digging
into these misconceptions, and I strongly urge anyone who wants to compare
software development to engineering to watch his talk <a href="https://www.youtube.com/watch?v=RhdlBHHimeM">Real Software Engineering</a>. It's also well worth
listening to <a href="https://podcast.oddly-influenced.dev/episodes/glenn-vanderburg-on-engineering">his interview on the podcast Oddly
Influenced.</a> Sadly I've not been able to persuade him to write this
material down - it would make a great article.</p>
<p>Another good thinker on this relationship is Hillel Wayne. He interviewed a
bunch of “crossovers” - people who had worked both in traditional engineering
and in software. He wrote up what he learned in a series of essays, starting
with <a href="https://www.hillelwayne.com/post/are-we-really-engineers/">Are We Really Engineers?</a></p>
Bliki: Test Driven Development2023-12-11T14:40:00-05:00https://martinfowler.com/bliki/TestDrivenDevelopment.html
<p>Test-Driven Development (TDD) is a technique for building
software that guides software development by writing tests. It was
developed by <a href="https://substack.com/@kentbeck">Kent
Beck</a> in the late 1990's as part of
Extreme Programming. In essence we follow three simple
steps repeatedly:</p>
<ul>
<li>Write a test for the next bit of functionality you want to add.</li>
<li>Write the functional code until the test passes.</li>
<li>Refactor both new and old code to make it well structured.</li>
</ul>
<div class="figure "><img src="https://martinfowler.com/bliki/images/test-driven-development/card.png">
<p class="photoCaption"></p>
</div>
<p>Although these three steps, often summarized as <i>Red - Green -
Refactor</i>, are the heart of the process, there's also a vital initial
step where we write out a list of test cases first. We then pick one of these
tests, apply red-green-refactor to it, and once we're done pick the next.
Sequencing the tests properly is a skill, we want to pick tests that drive us
quickly to the salient points in the design. During the process we should add
more tests to our lists as they occur to us.</p>
<p>Writing the test first, what <a href="https://www.amazon.com/gp/product/0321278658/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321278658&linkCode=as2&tag=martinfowlerc-20">XPE2</a> calls
Test-First Programming, provides two main benefits. Most obviously it's a way
to get <a href="/bliki/SelfTestingCode.html">SelfTestingCode</a>, since we can only write some functional
code in response to making a test pass. The second benefit is that thinking
about the test first forces us to think about the interface to the code first.
This focus on interface and how you use a class helps us separate interface
from implementation, a key element of good design that many programmers
struggle with.</p>
<p>The most common way that I hear to screw up TDD is neglecting
the third step. Refactoring the code to keep it clean is a key part
of the process, otherwise we just end up with a messy aggregation of
code fragments. (At least these will have tests, so it's a less
painful result than most failures of design.)</p>
<div class="furtherReading">
<h2>Further Reading</h2>
<p>Kent's summary of the <a href="https://tidyfirst.substack.com/p/canon-tdd">canonical way to do TDD</a>
is the key online summary.</p>
<p>For more depth, head to Kent Beck's book
<a href="https://www.amazon.com/gp/product/0321146530/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321146530&linkCode=as2&tag=martinfowlerc-20">Test-Driven Development</a>.</p>
<p>The relevant chapter of James Shore's <a href="https://www.amazon.com/gp/product/1492080691/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1492080691&linkCode=as2&tag=martinfowlerc-20">The
Art of Agile Development</a> is another sound description that also
connects it to the rest of effective agile development. James also wrote a
series of screencasts called <a href="http://www.jamesshore.com/Blog/Lets-Play">Let's Play TDD</a>.</p>
</div>
<div class="revisions">
<h2>Revisions</h2>
<p>My original post of this page was 2005-03-05. Inspired by Kent's
canonical post, I updated it on 2023-12-11</p>
</div>
Bliki: Diff Debugging2023-12-04T00:00:00-05:00https://martinfowler.com/bliki/DiffDebugging.html
<p>Regression bugs are newly appeared bugs in features of the software that have been around
for a while. When hunting them, it usually valuable to figure out which change
in the software caused them to appear. Looking at that change can give
invaluable clues about where the bug is and how to squash it. There isn't a
well-known term for this form of investigation, but I call it Diff Debugging.</p>
<p>Diff debugging only works if we have our code in version control, but
fortunately these days that's the norm. But there are some more things that
are needed to make it work effectively. We need <a href="/bliki/ReproducibleBuild.html">Reproducible Builds</a>, so that we can run old versions of
the software easily. It helps greatly to have small commits, due to <a href="/articles/branching-patterns.html#integration-frequency">high-frequency
integration</a>. That way when we find the guilty commit, we can more easily
narrow down what happened.</p>
<p>To find the commit that bred the bug, we begin by finding any past version
without the bug. Mark this as a <i>last-good</i> version and the current
version as the <i>earliest-bad</i>. Then find the commit half-way between the
two and see if the bug is there. If so then this commit becomes the earliest-bad,
otherwise it becomes the last-good. Repeat the process (which is a
“half-interval” or “binary” search) until we've got the guilty commit.</p>
<p>If we use git, then the <a href="https://git-scm.com/docs/git-bisect/">git
bisect</a> command will automate much of this for us. If we can write a test
that will show the presence of the bug, then git bisect can use that too,
automating the whole process of finding the guilty commit.</p>
<p>I often find diff debugging to be useful within a programming session. If I
have slow tests that take a few minutes to run, I might program for
half-an-hour running only a subset of the most relevant tests. As long as I
commit after every green test run, I can use diff debugging should one of
those slower tests fail. Such is the value of committing extremely frequently,
even if they are so small that I feel its best to squash them for the long-term
history. Some IDEs make this easier by keeping a local history automatically
that is finer-grained than the commits to version control.</p>
<div class="revisions">
<h2>Revisions</h2>
<p>I originally posted this page on 2004-06-01. In its original form it was
more of a casual experience report. I rewrote it on 2023-12-04 to make it
more like a definition of the term. Diff debugging isn't a term that's
caught on much in the industry, but I haven't seen a another term generally
used to describe it.</p>
</div>
How to tackle unreliability of coding assistants2023-11-28T10:21:00-05:00tag:martinfowler.com,2023-11-28:How-to-tackle-unreliability-of-coding-assistants
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-08'><img src = 'https://martinfowler.com/articles/exploring-gen-ai/assistant-persona.png' width = ''></img></a></div>
<p>Over the last year, lots of developers have incorporated LLM coding
assistants into their work, finding them a useful tool. But one of the
problems of these tools is that they are unreliable, often coming up with
poor or outright wrong-headed suggestions. <b class = 'author'>Birgitta
Böckeler</b> continues her exploration of GenAI for developers by
passing on what she's learned about <a href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-08'>how think about this unreliability</a>, and
why it may be good to call your LLM tool “Dusty”. </p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-08'>more…</a></p>Patterns of Distributed Systems is published by Pearson2023-11-24T14:11:00-05:00tag:martinfowler.com,2023-11-24:Patterns-of-Distributed-Systems-is-published-by-Pearson
<div class = 'img-link'><a href = 'https://martinfowler.com/books/patterns-distributed.html'><img src = 'https://martinfowler.com/books/joshi.jpg' width = '150px'></img></a></div>
<p>During the last four years, my colleague <b class = 'author'>Unmesh Joshi</b>
been developing a collection of patterns to help us all better understand
how modern distributed systems work. We've been publishing drafts of these
patterns on this site. Now these have turned into a <a href = 'https://martinfowler.com/books/patterns-distributed.html'>book, published
by Addison-Wesley</a> in my signature series. As such, we've now removed
the work-in-progress drafts from this site, and have replaced them with a
<a href = '/articles/patterns-of-distributed-systems/'>catalog of pattern
summaries</a>. For those with a subscription to oreilly.com, we have deep
links from the summaries to the relevant chapter of the online book.</p>
<p><a class = 'more' href = 'https://martinfowler.com/books/patterns-distributed.html'>more…</a></p>Three reasons a liberal arts degree helped me succeed in tech2023-11-09T10:12:00-05:00tag:martinfowler.com,2023-11-09:Three-reasons-a-liberal-arts-degree-helped-me-succeed-in-tech
<p>My colleague <b class = 'author'>Sannie Lee</b> has met many students who are looking into
getting into technology, taking narrow professionally-oriented majors.
Sannie, however, has found that a traditional liberal-arts degree <a href = 'https://martinfowler.com/articles/2023-liberal-arts.html'>has given
her skills</a> that are highly relevant to her work as a product manager.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/2023-liberal-arts.html'>more…</a></p>Enhancing the Headless Component2023-11-07T10:00:00-05:00tag:martinfowler.com,2023-11-07:Enhancing-the-Headless-Component
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/headless-component.html#AdaptingToANewUiRequirement'><img src = 'https://martinfowler.com/articles/headless-component/headless-component.png' width = ''></img></a></div>
<p>In the <a href = 'https://martinfowler.com/articles/headless-component.html#AdaptingToANewUiRequirement'>second (and final) part</a> of his explanation of React Headless Components
<b class = 'author'>Juntao Qiu</b> explores how a headless component allows us to
create a visually different component that does the same base behavior, and
how it encourages better factoring as we extend base behavior further.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/headless-component.html#AdaptingToANewUiRequirement'>more…</a></p>Current thoughts on social media2023-11-02T12:33:00-04:00tag:martinfowler.com,2023-11-02:Current-thoughts-on-social-media
<p>It's now been a year since The Muskover, what does my use of social
media look like now, both as a reader and a writer?</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/2023-social-media.html'>more…</a></p>Headless Component: a pattern for composing React UIs2023-11-01T10:34:00-04:00tag:martinfowler.com,2023-11-01:Headless-Component--a-pattern-for-composing-React-UIs
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/headless-component.html'><img src = 'https://martinfowler.com/articles/headless-component/headless-component.png' width = ''></img></a></div>
<p>As React UI controls become more sophisticated, complex logic can get
intertwined with the visual representation. This makes it hard to reason
about the behavior of the component, hard to test it, and necessary to
build similar components that need a different look. <b class = 'author'>Juntao
Qiu</b> tackles this by using a <a href = 'https://martinfowler.com/articles/headless-component.html'>Headless Component</a>, which
extracts all non-visual logic and state management, separating the brain of
a component from its looks.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/headless-component.html'>more…</a></p>How is GenAI different from other code generators?2023-09-19T08:57:00+01:00tag:martinfowler.com,2023-09-19:How-is-GenAI-different-from-other-code-generators-
<p>How is code generation with GenAI different from more "traditional" code
generators? The newest memo in <b class = 'author'>Birgitta Böckeler's</b>
explorations of GenAI talks about <a href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-07'>abstraction
levels</a> in software engineering, and on which levels GenAI sits in the
translation of our thoughts into zeros and ones.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-07'>more…</a></p>Technology Strategy for Emerging Technologies and Markets2023-08-24T09:44:00-04:00tag:martinfowler.com,2023-08-24:Technology-Strategy-for-Emerging-Technologies-and-Markets
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#RespondingToTheEverChangingFuture'><img src = 'https://martinfowler.com/articles/creating-integrated-tech-strategy/card.png' width = ''></img></a></div>
<p><b class = 'author'>Sarah Taraporewalla</b> completes her study of building a
technology strategy that's integrated with strategic business interests.
This final strategic direction considers <a href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#RespondingToTheEverChangingFuture'>the ever-changing future</a>,
suggesting lines of inquiry to consider the impact of new
technologies, market trends, and broader social-political changes. </p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#RespondingToTheEverChangingFuture'>more…</a></p>Demo Front-End: A front-end application to test and explore an API2023-08-23T10:37:00-04:00tag:martinfowler.com,2023-08-23:Demo-Front-End--A-front-end-application-to-test-and-explore-an-API
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/demo-front-end.html'><img src = 'https://martinfowler.com/articles/demo-front-end/card.png' width = ''></img></a></div>
<p>Many software teams create services exposed as APIs, designed to be
consumed by other software and thus without any user-interface. Such
services are hard to demonstrate, as they effectively just dump pages of
JSON. A <a href = 'https://martinfowler.com/articles/demo-front-end.html'>demo front-end</a> is a simple user-interface just used to
manipulate such an API. <b class = 'author'>Matteo Vaccari</b> describes how and
why to build one - showing its usefulness both in explaining the
API's capabilities to stakeholders and to help client developers explore
how to interact with the API.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/demo-front-end.html'>more…</a></p>Strategic Directions supporting the people2023-08-22T11:22:00-04:00tag:martinfowler.com,2023-08-22:Strategic-Directions-supporting-the-people
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#SupportingThePeople'><img src = 'https://martinfowler.com/articles/creating-integrated-tech-strategy/card.png' width = ''></img></a></div>
<p>Having a robust digital talent strategy is a competitive advantage in
today’s fiercely competitive market. This enables businesses to have the
right talent and have the right competencies to meet current and future
demand to meet business goals or to stay on track for digital
transformation aspirations. <b class = 'author'>Sarah Taraporewalla</b> continues
her article on how to create an integrated business and technology strategy
by looking at questions raised by <a href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#SupportingThePeople'>two strategic directions that
support people</a>: culture and internal systems.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/creating-integrated-tech-strategy.html#SupportingThePeople'>more…</a></p>Bottlenecks of Scaleups #05: Resilience and Observability2023-08-22T10:21:00-04:00tag:martinfowler.com,2023-08-22:Bottlenecks-of-Scaleups--05--Resilience-and-Observability
<div class = 'img-link'><a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/05-resilience-and-observability.html'><img src = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/card-resilience-observability.png' width = ''></img></a></div>
<p>Here is a new article in the bottlenecks of scaleups series, looking at
resilience and observability. Startups tend to only address resilience when
their systems are already down, often taking a very reactive approach. For
a scaleup, <a href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/05-resilience-and-observability.html'>excessive system downtime represents a significant
bottleneck</a> to the organization, both from the effort expended on
restoring function and also from the impact of customer dissatisfaction.
<b class = 'author'>Punit Lad</b> and <b class = 'author'>Carl Nygard</b> explain that to
move past this, resilience needs to be built into the business objectives,
which will influence the architecture, design, product management, and even
governance of business systems. </p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/bottlenecks-of-scaleups/05-resilience-and-observability.html'>more…</a></p>TDD with GitHub Copilot2023-08-17T14:44:00-04:00tag:martinfowler.com,2023-08-17:TDD-with-GitHub-Copilot
<p>At Thoughtworks, we are strong practitioners of <a href = '/bliki/TestDrivenDevelopment.html'>Test Driven Development</a> (TDD).
Naturally this leads to the question of how generative AI can help with
this technique. <b class = 'author'>Paul Sobocinski</b> writes a brief memo
explaining how some of our teams have used <a href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-06'>TDD with GitHub
Copilot</a>. As ever, co-pilot can't be relied on to fly the plane, but
can suggest some useful ideas for the red and green steps. It isn't
very helpful for the all-important refactoring step.</p>
<p><a class = 'more' href = 'https://martinfowler.com/articles/exploring-gen-ai.html#memo-06'>more…</a></p>