The long way through Software Craftsmanship

Destructuring as a refactor in Clojure

Apr 14, 2015 - 1 minute read - Comments - clojurerefactordestructuringparallel-change

Manuel has taught us today about the default value while destructuring:

The following example illustrates the use of an :as directive to bind a local with the entire map.

user=> (def point {:x 5 :y 7})
#'user/point

(let [{:keys [x y] :as the-point} point]
         (println "x:" x "y:" y "point:" the-point))

x: 5 y: 7 point: {:x 5, :y 7}

We’ve now seen the :as directive used for both vectors and maps. In both cases the local is always assigned to the entire expression that is being destructured.

Source

This has been used for a method like this one:

(defn all-access[k]
  (do (:a k))
  (do (:b k)))

this has been refactored to this

(defn all-access[k]
  (let [{:keys [a b] :as k}]
	  (do a)
	  (do (:b k))))

the best thing about this is I can introduce the keys to the array while keeping the original map (k) and, when I’m ready, to change some of the accesses to k to its destructured variables.

Redesign as a new TDD phase

Apr 13, 2015 - 2 minute read - Comments - redesigntddtheory

In the last TDD workshop (experience report here), a conversation with Gary McLean Hall introduced to me this new concept of “Redesign” as a TDD phase.

Concept

As Gary introduced it to me1, it is a phase that might appear after refactor.

It is about changing the outside design without changing the expected behavior. I thought this was also included in the refactor phase.

How I do redesign

I usually do this “changing of the outside design” by applying a series of refactors 2 to the production code but not changing the test code; using a bridge / adapter to get to the new API from the old one. Later you can inline the scaffold and use the new API directly.

The same for the test code: when you want to change design in the test code, do not modify production code (a.k.a. model code).

Other thoughts

Merriam-Webster defines “redesign” as

to change the design of (something)

or

to revise in appearance, function, or content

an example:

The book’s cover has been redesigned for the new edition.

The Refactoring book (by Martin Fowler) describes “refactoring” as

Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure

Taken literally, the public API is not internal but external structure, so changing it, should belong to a phase that is not refactor (see definition), nor red (no failing tests), not green (making it pass). Maybe this phase is “redesign” or we have to take the refactoring phase less literally so it includes redesign. Any thoughts?

Other references

I’ve searched for redesign and refactor and this blog post came up. They cite redesign as a way of rewriting

This answer and the original question in StackOverflow is interesting

Redesign also appears cited in this blog post


  1. Excuse me if you explained it differently and I understand it wrong; you can always open a pull request to fix it here ↩︎

  2. thanks for the clarifying refactor vs refactoring here ↩︎

Experience report: Test-driven development intensive, by Jason Gorman

Apr 12, 2015 - 8 minute read - Comments - trainingworkshopexperience-reportjason-gormanuktddcrc-cardpair-programming

This is an experience report on Jason Gorman’s “Test-driven development intensive workshop” on the April 11th, 2015

I attended this training in the Greenfell housing & training in South Wimbledon, London SW19 1JZ. The training spanned from 10:00 to 17:00 with a half an hour for lunch plus three or four 10-15 minute pauses. (7 - ,5 - 4 * 12/60 = ~5.7hours = 5 hours 45 minutes)

Introduction

There were 23 of us, including me. We spent some half hour introducing ourselves before the training started. I remember about a tester that came to the workshop “wanting to know more about this testing methodology”. Jason’s answer was priceless: “I’m sorry but you came to the wrong place: this is not about testing”.

At 10:00 sharp, he started a set of slides explaining what TDD is about, including:

  • productivity:
    • no company is willing to give you a “get out of jail free” card to get out of the productivity zone
    • a drop in the productivity, at least for the first weeks
    • you have to find a time slot in your schedule to practice TDD, otherwise it’s like learning to swim while crossing the Atlantic Ocean
  • You have to feel uncomfortable not doing TDD
  • triangulation:
    • how each problem is different
    • you can only learn it by doing
  • TDD styles:
  • 13 good habits for a sustainable test-driven development way
  • The basic TDD cycle:
    • an executable specification, encoded in a failing test
    • pass the test in the simplest way
    • refactor
    • (talking with another attendee, he described to me a new TDD phase called redesign. Maybe another post will explain this in deeper detail level)
  • Some ideas about Simple design, simple development, simplicity (XP)
  • Shown a wrong example of mixing assertions (state) and verifications (interactions)
  • More than one reason to go wrong makes the code non-trivial anymore. Non-trivial code requires tests
  • You want to see the test failing for the right reason: remove / finish code that does not compile, fix null pointers, array out of bounds exceptions, etc. See it fail with “was but expected ” or “NoInteractionException” (hamcrest error and mockito error, respectively; both java)
  • Duplication as a sign of design smell
  • Process of generalizing code: discover the need for patterns
  • The most difficult skill in TDD is refactoring [and design; note is mine]. The TDD cycle is very easy.
    • TDD is much more in demand than refactoring, even though the former includes the latter
  • Refactor until you’re happy with the code, until you stand by it

CRC Cards

A Class-responsibility-collaboration card is as follows:

  • top: class name
  • half left: responsibilities
  • half right: collaborations

Notes from the slides:

  • each then is an outcome
    • do not mix outcomes and implications. An example: after winning the lottery, the money should be transferred to my bank account and I can buy a yacht. The former is an outcome (change in state) and the latter an implication (a new action that is now enabled)
    • an outcome is mapped to an assertion, while a collaboration is mapped to an interaction (verify in mockito)
  • Given / When / Then is not enough to implement an executable specification. We need examples, detail
  • OO: send messages to distribute responsibility.
    • A Then is an unique responsibility
  • Topmost object: no one connects with it
  • Put the work where the data is
  • Tell, don’t ask 1 vs data driven design (tell a collaborator what to do vs ask for values and do the work yourself)
  • You want as few interactions as possible (related to the tell, don’t ask)
  • More objects than outcomes is a bad thing (design smell) (?)
  • CRC is a very mechanical exercise, do not over think it
  • Describe outcomes as changes in OO (new, destroy, relationships, modify fields / state)
  • Continuous integration is not necessarily about shipping the code at each commit but having it shippable at each commit, as always having a product that is ready for its production phase

Exercises

All exercises were done in pairs, always switching one half of the pair. He asked for someone in the pair to raise, especially if you were not a programmer (e.g., tester, manager)

First exercise: Banking

Write a program that is capable of transferring money from an account to another

A chance to practice arrange, act, assert

Later, he did it some live coding to develop the same kata, practicing “TDD as if you meant it” (Keith Braithwaite’s original idea). We discovered together the “primitive obsession” and “feature envy” smells. We tried doing as few decisions as possible.

Second exercise: Fibonacci sequence generator

Write a program that is capable of generating Fibonacci sequences, no shorter that 8 and no longer than 50

A chance to practice baby steps.

My pair and I had the chance of discovering the importance of understanding correctly the requirements before writing a line of code. Also, I learned about the different ways of doing TDD. In this case, my pair executed the tests via a main (java class) and saw the failures on the console. When proposed to do it in the JUnit runner the IDE has, he said he preferred it in his own ways.

Jason did some live coding to show how he approached and triangulated this exercise.

I really liked some of what he said: I like to make the API correct from the first moment so I will ask questions that are correct but only check answers for things that I have already implemented. Example:

As the generated sequence can only have 8 <= x <= 50 elements, a list returning a single element would not be correct. So

//class FibonacciTests
@Test
public void theFirstElementIsOne(){
	assertThat(new FibonacciSequence().generate(8)[0], is(1));
}

Third exercise: FizzBuzz

Write a program that is capable of returning a string with all numbers less than 100 that are using the FizzBuzz pattern. This FizzBuzz pattern converts the divisors of 3 to ‘fizz’, divisors of 5 to ‘buzz’ and divisors to both to ‘fizzbuzz’

I discovered that this FizzBuzz is a drinking game in the UK.

Fourth exercise: CRC cards

Before this exercise there was a short introduction on Class-responsibility-collaboration card, explained above

Following an example about a movie library, there were six user stories, described at high level, without acceptance criteria.

We had to create an acceptance criteria for this user story. Later create a set of CRC cards and finally partially implement said feature.

Very interesting exercise, such a pity that there wasn’t more time to complete this exercise or see him doing it

Knowledge pearls

He dropped some knowledge pearls (that I’m going to use myself):

Q: When are you done with TDD?
A: When you can not think of more tests for your suite
Q: What do we do after TDD?
A: (No answer)
Q: Starts with "T". Any idea?
A: Testing. Test for the purpose of testing, not TDD

Close concepts interested in responsibilities:

  • Conceptual diagram and class diagram
  • Instance diagram
  • Knowledge map and tag cloud

Regarding legacy code:

  • Isolate big balls of mud into parts and test these as end-to-end. Also connected to isolating groups of nodes into the class graph
  • Understand what the code does, not the architecture
  • Do not use a mock to help you test legacy code, use it just as a design tool. (It might be a good idea to use it temporarily while you refactor, but remove it afterwards)
  • Tests with mocks double down on the design: if it is broken, it will be a drag more than help

Test on the boundaries. Many times we test in the middle of the algorithm but forget the boundaries, where many special cases hide

The GOOS book is pronounced like the goose animal ("/gús/") in English

He said he never copy pastes 2.

Conclusions

  • This is a two day workshop condensed on a single day. It shows. In my opinion, it would be better to do fewer exercises and dig deeper in them rather than try so many approaches / exercises
  • The CRC exercise was well thought and really massaged. It touches the right spots and makes you want to learn more about the subject
  • Promiscuously pairing is always a good idea, as you don’t know what level have your attendees. Get stuck for the whole day with someone who has a (very) different knowledge level than you might spoil the workshop
  • I don’t see the benefit of doing the fizzbuzz kata after having done the Fibonacci sequence one
  • In my opinion, there were too many breaks. Doing a break in the morning (10-15 mins), lunch (30 mins) and afternoon (10-15 mins) is enough

On a personal note:

  • It is not enough to attend the workshop. You have to take notes and study them at home later. (Thanks spines for this lesson)
  • I really had a great time, learned a lot, even though I already know something about TDD
  • Met very interesting people
  • Would attend to the workshop again

In general, I’d like to thank Jason and the other attendees for making this workshop possible. I had a blast there.


  1. As described by Martin Fowler and the Pragmatic Bookshelf ↩︎

  2. Copy pasting this post’s structure resulted in a wrong title that had to be fixed. Maybe the extra effort of not copy pasting has a good ROI, as long as you do not copy paste by typing the text you’re reading from somewhere else ↩︎

Trimming your plants

Apr 2, 2015 - 2 minute read - Comments - growing-softwaretrimmingagriculturegardeningsoftwarerefactorrefactoringgoos

This post belongs to the growing-software series

Imagine this situation: the code works. You are at this stage

Disregarding the tests, the production code is complete. Could be better, but it is ready if you are in a hurry.

Now, picture your test as a plant in a plant pot: it might be healthy on the outside (external quality) but how are their roots (internal quality)?

Do you imagine them in an ordered fashion or in a jungle of roots? Everything on the outside is connected to one or several roots? In case it is several, what / how much do they share?

Trimming the roots

Now, let’s imagine you want to put the plant in a different plant pot. You don’t want the plant to be altered in any way: keep producing fruits as it was before, as green and shiny, etc. Just alter the container for business reasons.

The more tidy and organized the roots are related to the outside plant itself, the easier it is to reshape (or refactor) it.

So this is exactly what refactoring is about: “keeping the same external behaviour, modifying the internal structure”. I like to explain as “modify how it is done but not what it does”.

When you trim the roots but leave unaltered the top, you are making it cheaper to modify the internal shape without affecting the rest. The plant will continue to grow, requiring more and more nutrients from the ground. Small increases that do not warrant a bigger plant pot, as the current one can withstand the new requirements.

This is where the analogy is no longer useful: when writing software, you’re also making it easier to reuse, modify and maintain.

Conclusion

The more tidied up you have your plants, the cheaper to maintain and modify.

Software implementation details are like roots: they expand to find any small hole where to expand. Even to scaffold expected behaviour

Trim your plants early and often, always with the help of a test suite

TDD microexercise: Tire pressure

Mar 31, 2015 - 2 minute read - Comments - microexerciseprepared-katasessionscbcn

Yesterday, Manuel Rivero and I facilitated a workshop using the tire pressure TDD microexercise. (More microexercises can be found in this category, also here)

We scheduled the session as follows:

  • 10 minutes introduction to the exercise
  • 10 minutes to download the code and take a first look
  • 10 minutes explanation about the exercise, what the problem is, etc
  • 30 minutes for the attendees to explore the solution (on their own)
  • 10 minutes for commenting the explored solutions, comments about it
  • 40 minutes of prepared kata format
  • 10 minutes of final explanations, questions and answers (Q&A)

Total: 120 minutes or two hours, approximately

We decided that one would be mostly at the keyboard and the other mostly explaining. This allows for the speaker to introduce some concepts while the driver is performing a larger refactor / step.

Some of our own feedback:

  • Have the kata prepared, clear all your doubts before doing it live
  • Be very specific about the goal of the kata: the narrower the focus, the more we will learn
  • If necessary, exercise your shortcuts / practices one last time
  • Set up the font as big as needed. Even remove some whitelines (present in your usual code) so more text can fit in the screen.
  • It was very useful for us to have a small script of what we would perform: what was the idea, where to stop to explain things, etc
  • A prepared kata is a show (like a movie), so the dialogs between the performing pair are important. Do not be afraid to tell the reasons why you did this last refactor.
  • Answer the questions as you go, do not wait until the end of the session

Some feedback we received:

  • It would have been useful to do a recap at the end of the session, to refresh the most important concepts
  • In general the session was good, they were satisfied with the techniques shown there
  • The exercise was concise and small, perfect for two hours' time

Many thanks to all the attendees and to Manuel: I learnt a lot from the session and renewed my energies to keep doing these workshop sessions. Also thanks to Luca Minudel for writing these awesome exercises.