Programmers
We perform small, isolated experiments to inform our decisions.
You’ve probably noticed how much Agile teams value concrete data over speculation. Whenever you’re faced with a question, don’t speculate about the answer—conduct an experiment! Figure out how you can use real data to make progress.
That’s what spike solutions are for, too. A spike solution, or spike, is a technical investigation. It’s a small experiment, in code, to research the answer to a problem. It usually takes less than a day. When you have the answer, the spike is discarded.
People often confuse spike solutions with walking skeletons: bare-bones code that demonstrates an idea from end-to-end. It’s the beginnings of a production implementation. In contrast, a spike is narrowly focused on a specific technical problem, and it’s thrown away afterward.
Spike solutions use code because nothing is more concrete. You can read as many books, tutorials, or online answers as you like, but to truly understand a solution, write working code. It’s important to work from a practical point of view, not just a theoretical one. The best way to do so depends on what you want to learn.
For questions about your language, libraries, or tools, write a line or two of code. If your programming language has a REPL (an interactive programming prompt), that’s often the quickest way to get your answer. For example, if you wanted to know if JavaScript could use comparison operators on strings, you could open a web browser console:
> "a" < "b" true > "a" > "b" false > "a" === "a" true
Alternatively, you can write a short test. You can put it right next to your real tests, then delete it afterward. For example, if you wanted to know if Java throws an exception on arithmetic overflow, a throwaway test would answer the question:
@TestpublicvoiddeleteMe(){inta=Integer.MAX_VALUE+1;// test will fail if exception thrownSystem.out.println("No exception: a = "+a);}// Result of test run: "No exception: a = -2147483648"
To learn how to use a third-party dependency, such as a library, framework, or service, create a small, standalone program to explore how the dependency works. Don’t bother writing production-grade code—just focus on demonstrating the core idea. Run from the command line, hardcode values, and ignore user input. Provide just enough design and abstraction to keep yourself from getting lost.
For complex dependencies, such as frameworks, I’ll often start with their tutorial. However, those tutorials tend to emphasize getting up and running quickly, not helping you understand the framework. They often have a lot of magic tooling that makes the framework harder to understand, not easier. So make their example your own. Remove magic, call APIs manually, and simplify unneeded complexity. Think about your use cases and demonstrate how they’ll work.
When you’re done, you can check the spike into your code repository to act as a reference while you build the real implementation. (I use a /spikes directory.) Once you’ve built out the production implementation, you can either delete the spike or keep it for future reference, depending on how useful it is.
If you have an idea for a design improvement, but you’re not sure how it will work out, you can spike the design. I’ll use this approach when I’m not sure if my design ideas will work as well as I think.
To spike a design, create a temporary, throwaway branch in your repository. In that temporary branch, you can experiment without having to worry about safe refactorings or passing tests. You don’t even need the code to work properly. The purpose of the spike is just to experiment with your design idea and see how it works in practice.
If your design idea doesn’t work out, delete the branch. If it does work out, you can keep it for reference, temporarily, but don’t merge it into your real code. Redo the change from scratch, this time taking care with your refactorings and updating tests as needed. When you’re done, delete the branch.
Avoid overusing design spikes. Although you’re welcome to create a design spike whenever it will help you understand your design options, they shouldn’t be necessary for every story. You should also be able to create new designs by starting with a simple, obvious approach that incrementally becomes more sophisticated, and you should be able to modify existing designs using reflective design.
Small, “quick question” spikes are usually performed on the spur of the moment. You see a need to clarify a small technical issue, you write and delete a quick spike, you move on.
Dependency and design spikes can happen in several ways. Sometimes, they’re planned intentionally, either with a spike story or a task. At other times, you won’t realize a story needs a spike until you’re in the middle of working on it. When that happens, you can either add a task to your planning board, or just work on the spike as part of your current task. Either way, your slack absorbs the cost.
What’s the difference between a prototype and a spike?
“Prototype” doesn’t have a strict definition, but it usually refers to incomplete or nonfunctioning software that’s made to mimic the final product. They’re often used to demonstrate UIs or to learn by building a throwaway version of the application.
Spikes are much more focused. They’re created to answer a narrow technical question, not to mimic the final product.
Should we pair or mob on spikes?
It’s up to you. Because spikes don’t need to be maintained, even teams with strict pair programming rules don’t require writing spikes in pairs.
One very effective way to pair or mob on a spike is to have one person research the technology while another codes. Another option is for people to work independently on separate approaches, each doing their own research and coding, then coming together to review progress and share ideas.
Should we really throw away our spikes?
Unless you think someone will refer to it later, toss it. Remember, the purpose of a spike solution is to give you the information and experience needed to solve a problem, not to produce the code that solves it. The real production code usually ends up being a better reference than the spike.
When should we create a spike?
Whenever it helps. Perform a spike whenever the constraints of writing production-grade code get in the way of figuring out a solution.
What if the spike reveals that the problem is more difficult than we thought?
That’s good; now you have information you needed to know. Perhaps your on-site customers will reconsider the value of the story you’re working on, or perhaps you need to think of another way to accomplish your goal.
Avoid the temptation to create useful or generic programs out of your spikes. Focus your work on answering a specific technical question, and stop working on the spike as soon as it answers that question. Similarly, there’s no need to create a spike when you already understand a technology well.
Don’t use spikes as an excuse to avoid disciplined test-driven development and refactoring. Never copy spike code into production code. Even if the spike does exactly what you need, rewrite it using test-driven development so that it meets your production code standards.
Spike solutions are a learning technique based on performing small, concrete experiments. Some people perform these experiments in their production code, which increases the scope of possible error. If something doesn’t work as expected, is it because your understanding of the technology is wrong? Or is it due to an unseen interaction with the production code? Standalone spikes eliminate this uncertainty.
An alternative to spike solutions is to research problems by performing web searches, reading theory, and finding code snippets online. This can be good enough for small problems, but for bigger problems, the best way to really understand the technology is to get your hands dirty. Go ahead and start with code you find online, if you need to, but then simplify and adapt the example. Why does it work? What happens when you change default parameters? Use the spike to clarify your understanding.
Another alternative, specifically for learning how to use third-party dependencies, is to start by writing test code that exercises the dependency. As you learn how the dependency works, refactor your experiment into a “test” and “implementation” portion, then move the implementation into your production code. This approach starts off like a spike, but morphs into high-quality, tested production code. Episode 5 of [Shore2020b] demonstrates the technique, starting at 13:50, and episode 17 has a larger example.