-
Notifications
You must be signed in to change notification settings - Fork 0
/
tdd-rdd-and-ddd.html
65 lines (65 loc) · 7.25 KB
/
tdd-rdd-and-ddd.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<head><title>TDD, RDD and DDD</title><link href="tufte-css/tufte.css" rel="stylesheet" /><script src="http:https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script></head><body><h1>TDD, RDD and DDD</h1><p class="subtitle"><a href="about-me.html">John Jacobsen</a></p><div><p><strong>This is the third post in a series about my current
Clojure workflow.</strong></p><p><span>Having <a href="emacs-customization-for-clojure.html">discussed Emacs setup</a> for Clojure, I now present a sort
of idealized workflow, in which I supplement traditional TDD with
literate programming and REPL experimentation.</span></p><p>First, some questions:</p><p><ol type="a"><li>How do you preserve the ability to make improvements without
fear of breaking things?</li><li>What process best facilitates careful thinking about design
and implementation?</li><li>How do you communicate your intentions to future
maintainers (including future versions of yourself)?</li><li>How do you experiment with potential approaches and solve
low-level tactical problems as quickly as possible?</li><li>Since “simplicity is a prerequisite for
reliability” (Dijkstra), how do you arrive at simple designs and
implementations?</li></ol></p><p><span>The answer to (a) is, of course, by having good tests; and the best
way I know of to maintain good tests is by writing test code
along with, and slightly ahead of, the production code (test-driven
development, a.k.a. TDD). However, my experience with TDD is that it
doesn’t always help much with the other points on the list (though
it helps a bit with (b), (c) and (e)). In particular, <a href="http:https://www.infoq.com/presentations/Simple-Made-Easy">Rich Hickey points out</a> that TDD is not a substitute
for <em>thinking</em> about the problem at hand.</span></p><p><span>As an aid for thinking, I find writing to be invaluable, so a
minimal sort of <a href="http:https://en.wikipedia.org/wiki/Literate_programming">literate programming</a> has become a part of my workflow, at
least for hard problems.</span></p><p><h1>The Workflow</h1><p>Now for the workflow proper. Given the following tools:</p><p><ol><li>Emacs + Cider REPL</li><li>Unit tests <a href="testing,-continuously.html">running continuously</a></li><li><a href="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/MichaelBlume/marginalia">Marginalia</a> running continuously, via <a href="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/eigenhombre/continuous-testing-helper">conttest</a>,</li></ol></p><p><span>then my workflow, in its <a href="http:https://en.wikipedia.org/wiki/Theory_of_Forms">Platonic Form</a>, is:</span></p><p><ol><li><strong>Is the path forward clear enough to write the next failing
test?</strong> If not, go to step 2. If it is, go to step 3.</li><li><a href="https://www.youtube.com/watch?v=f84n5oFoZBc">Think</a> and <strong>write</strong> (see below) about the problem. Go to 1.</li><li><strong>Write the next failing test</strong>. This test, when made to pass, should represent the smallest
“natural” increase of functionality.</li><li><strong>Is it clear how to make the test pass?</strong> If not, go to step 5. If it is, write the simplest
“production” code which makes all tests pass. Go to 6.</li><li><strong>Think, write, and conduct REPL experiments</strong>. Go to 4.</li><li><strong>Is the code as clean, clear, and simple as possible?</strong> If so, go to step 7. If not, refactor, continuously making sure
tests continue to pass with every change. Go to 6.</li><li>Review the Marginalia docs. <strong>Is the intent of the code
clear?</strong> If so, go to step 1. If not, <strong>write some more</strong>. Go to 7.</li></ol></p><p>“Writing” in each case above refers to updating comments and
docstrings, as described in a subsequent post on Literate
Programming.</p><p>Here are the above steps as a flow chart:</p><p><figure><a href="img/workflow.png"><img src="img/workflow.png" /></a></figure></p><p><span>The workflow presented above is a somewhat idealized version of
what I actually manage to pull off during any given coding
session. It is essentially the <span style="color:red">red</span>-<span style="color:green">green</span>-<span style="color:blue">refactor</span> of traditional
test-driven development, with the explicit addition of REPL
experimentation (“REPL-driven development,” or RDD) and continuous
writing of documentation (“documentation-driven development,” or
DDD) as a way of steering the effort and clarifying the
approach.</span></p><p><span>The utility of the REPL needs no elaboration to Clojure
enthusiasts and I won’t belabor the point here. Furthermore, a lot
has been written about test-first development and its advantages
or drawbacks. At the moment, <a href="http:https://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html">the practice seems to be particularly controversial</a> in the
Rails community. I don’t want to go too deep into the pros and
cons of TDD other than to say <a href="continuous-testing-in-python,-clojure-and-blub.html">once
again</a> that the practice has
saved my bacon so many times that I try to minimize the amount
of code I write that doesn’t begin life as a response to a
failing test.</span></p><p>What I want to emphasize here is how writing and the use of the
REPL complement TDD. These three ingredients cover all the
bases (a)-(e), above. While I’ve been combining unit tests and the
REPL for some time, the emphasis on writing is new to me, and I am
excited about it. Much more than coding by itself, I find that
writing things down and building small narratives of code and
prose together forces me to do the thinking I need in order to
write the best code I can.</p><p><h3>Always Beginning Again</h3><p>While I don’t always follow each of the above steps to the
letter, the harder the problem, the more closely I will tend to
follow this plan, with one further modification: I am willing to
wipe the slate clean and begin again if new understanding shows
that the current path is unworkable, or leads to unneeded
complexity.</p><p><span>The next few posts attack specifics about <a href="testing,-continuously.html">testing</a> and <a href="communicating-with-humans.html">writing</a>, presenting what I personally have found most effective (so far),
and elaborating on helpful aspects of each.</span></p></p></p></div><div><p><a href="about-me.html">about</a>|<a href="content.html">all posts</a></p><p>© 2016 <a href="about-me.html">John Jacobsen</a>. Created with <a href="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/eigenhombre/unmark">unmark</a>. CSS by <a href="https://edwardtufte.github.io/tufte-css/">Tufte-CSS</a>.</p></div><script type="text/javascript">var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-40279882-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http:https://www')
+ '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();</script></body>