Skip to content

Commit 506c997

Browse files
committed
Lots of new material fleshing out code design process on phase1 page, along with tweaks on a few other pages
1 parent c35fd49 commit 506c997

File tree

3 files changed

+104
-21
lines changed

3 files changed

+104
-21
lines changed

_docs/phase1.rst

+93-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
Spaghetti
2-
=========
1+
(Re)designing code
2+
==================
33

4-
We begin with a single, linear script in the *elex1/* directory. Below
5-
are a few reasons why this `code
6-
smells <http://en.wikipedia.org/wiki/Code_smell>`__ (some might say it
4+
Code smells
5+
-----------
6+
7+
We begin with a single, linear script in the `elex1/ <https://github.com/PythonJournos/refactoring101/tree/master/elex1>`__ directory.
8+
Below are a few reasons why this `code smells <http://en.wikipedia.org/wiki/Code_smell>`__ (some might say it
79
reeks):
810

911
- It's hard to understand. You have to read the entire script before
@@ -13,6 +15,86 @@ reeks):
1315
file.
1416
- None of the code is reusable by other programs.
1517

18+
Hacking-It-Out-As-You-Go
19+
------------------------
20+
21+
Scripts like this are often born when a programmer dives immediately into implementing
22+
his code. He sees the end goal -- "summarize election data" -- and gets right to it, hacking
23+
his way through each step of the script until things "work".
24+
25+
The hack-it-out-as-you-go approach can certainly produce working code. But unless you're extremely disciplined,
26+
this process can also yield spaghetti code -- a jumble of hard-to-decipher and error-prone logic that you fear changing.
27+
28+
So, how do we avoid spaghetti code? *By choosing to have lots of small problems instead of one big problem.*
29+
30+
Lots of small problems
31+
----------------------
32+
33+
A key step in the art of designing code is hitting the breaks up front and spending a few minutes thinking
34+
through the problem at hand. Using this approach, you'll quickly discover that you don't really
35+
have one big problem (*"summarize some election data"*) but a series of small problems:
36+
37+
* Download election data
38+
* Parse election data
39+
* Calculate candidate vote totals and determine winners
40+
* Create a summary spreadsheet
41+
42+
Each of those smaller problems, in turn, can often be decomposed into a series of smaller steps, some of
43+
which don't become clear until you've started writing the code.
44+
45+
But it's critical at this phase to *NOT* start writing code!!! You will be tempted, but doing so will
46+
switch your brain from "design mode" to the more myopic "code" mode (it's a thing). Trust in your ability
47+
to implement the code when the time is right (we promise, you'll figure it out), and instead grant yourself a
48+
few minutes of freedom *to design the code*.
49+
50+
If you just can't resist implementing code as you design, then close your laptop
51+
and go old school with pen and paper. A mind-map or flow-chart is a great way to hash out the
52+
high-level design and flow of your program. Or if you're lucky enough to have a whiteboard,
53+
use that to sketch out the initial steps of your program.
54+
55+
Some folks also like writing `pseudocode <http://en.wikipedia.org/wiki/Pseudocode>`__,
56+
though beware the siren call's temptation to slip back into implementing "working" code
57+
(Python in particular makes this extremely easy).
58+
59+
*Fun fact*: Jeremy and I are so enthusiastic about whiteboarding that we once sketched out a
60+
backyard goat roast on an office wall (said design was never implemented).
61+
62+
Shred this code (on paper)
63+
--------------------------
64+
65+
In this tutorial, we already have some ready-baked spaghetti code for you to slice and dice into smaller components.
66+
67+
We encourage you to print the code on paper -- yes, dead trees! -- and use a marker to group code bits
68+
into separate functions. As you to try to make sense of the logic and data structures, it's a good idea to reference the
69+
`source data <https://docs.google.com/spreadsheet/pub?key=0AhhC0IWaObRqdGFkUW1kUmp2ZlZjUjdTYV9lNFJ5RHc&output=html>`__.
70+
71+
This exercise is intended to familiarize you with the data and the mechanics of the code, and get your
72+
creative juices flowing. As you read the code, think about which sections of logic are related (perhaps they
73+
process some piece of data, or apply a process to a bunch of data in a loop).
74+
75+
Use circles, brackets, arrows -- whatever marks on paper you need to group together such related bits of code.
76+
Then, try to give them *meaningful names*. These names will become the functions that wrap these bits of logic.
77+
78+
`Naming things is hard <http://martinfowler.com/bliki/TwoHardThings.html>`__, and can become *really hard* if a function is trying to do too many things.
79+
If you find yourself struggling to come up with a clear function name, ask yourself if breaking down the section of code into even smaller parts (
80+
say two or three functions instead of one) would make it easier to assign a clear and meaningful name to each function.
81+
82+
Finally, spend some time thinking about how all these new bits of code will interact. Will one of the functions require an input that comes
83+
from another function? This orchestration of code is typically handled in a function called `main <http://en.wikipedia.org/wiki/Entry_point>`__,
84+
which serves as the entry point and quarterback for the entire script.
85+
86+
Keep in mind there's no "right" approach or solution here. The overarching goal is to improve the *readability of the code*.
87+
88+
Whether you resort to pseudocode, a whiteboard, or simple pen-on-paper, the point is to stop thinking
89+
about *how to implement the code* and instead focus on *how to design the program*.
90+
91+
Once the code design process is complete, try implementing the design. Ask yourself how this process compared to prior efforts to
92+
write a script (or unravel someone else's code). Was it easier? Harder? Is the end product easier to read and understand?
93+
94+
In the next section, you'll see our pass at the same exercise, and learn how to further improve this script by organizing functions into
95+
new source files.
96+
97+
1698
Questions
1799
---------
18100

@@ -28,6 +110,9 @@ Exercises
28110
---------
29111

30112
- Slice up this code into a bunch of functions, where related bits of
31-
logic are grouped together.
32-
- Write a unit test for one or more functions extracted from this
33-
module.
113+
logic are grouped together. Do function names accurately reflect what they
114+
actually do? If not, how could you rename functions and/or re-organize the code
115+
to clarify the purpose of each function?
116+
- Compare your revised script to `our version <https://github.com/PythonJournos/refactoring101/blob/master/elex2/election_results.py>`__.
117+
What's similar? What's different? Explain 5 things you like or dislike about each and why.
118+
- Write a unit test for one or more functions extracted from this module.

_docs/phase2.rst

+4-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ We've also added a suite of tests. This way we can methodically change
99
the underlying code in later phases, while having greater confidence
1010
that we haven't corrupted our summary numbers.
1111

12-
We can't stress this step enough: Testing existing code is *the*
13-
critical first step in refactoring. If the code doesn't have tests,
14-
write some, at least for the most important bits of logic. Otherwise
15-
you're just `changing
16-
shit <http://hamletdarcy.blogspot.com/2009/06/forgotten-refactorings.html>`__
17-
(see Ray Ozzie).
12+
**Note**: We can't stress this step enough: Testing existing code is *THE* critical first step in refactoring.
13+
14+
If the code doesn't have tests, write some, at least for the most important bits of logic.
15+
Otherwise you're just `changing shit <http://hamletdarcy.blogspot.com/2009/06/forgotten-refactorings.html>`__.
1816

1917
Fortunately, our code has a suite of `unit
2018
tests <http://docs.python.org/2/library/unittest.html>`__ for name

_docs/phase4/races.rst

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ move on to building out the `Race` class. This higher-level
66
class will manage updates to our candidate instances, along with metadata
77
about the race itself such as election date and office/district.
88

9-
Recall that in *elex3*, the *lib/parsery.py* ensured that county-level results were assigned to the appropriate candidate.
9+
Recall that in *elex3*, the *lib/parser.py* ensured that county-level results were assigned to the appropriate candidate.
1010
We'll now migrate that logic over to the `Race` class, along with a few other repsonsibilities:
1111

1212
- Tracking overall vote count for the race
1313
- Updating candidates with new county-level votes
1414
- Determining which candidate, if any, won the race
1515

16-
Total votes
17-
-----------
16+
Metadata and Total votes
17+
------------------------
1818

1919
The *Race* class keeps a running tally of all votes. This figure is
2020
the sum of all county-level votes received by individual candidates.
@@ -70,8 +70,8 @@ method to make the tests pass.
7070
def add_result(self, result):
7171
self.total_votes += result['votes']
7272

73-
Candidate updates
74-
-----------------
73+
Candidate Bookkeeping
74+
---------------------
7575

7676
In earlier phases of the project, the parser code ensured that
7777
county-level results were grouped with the appropriate, unique candidate
@@ -188,8 +188,8 @@ count is correct.
188188
our hand at that issue in this tutorial and leave it as a study
189189
exercise for the reader.
190190

191-
Winner
192-
------
191+
Assigning Winners
192+
-----------------
193193

194194
We're now ready for the last major piece of the puzzle, namely,
195195
migrating the code that determines race winners. This logic was

0 commit comments

Comments
 (0)