The Skeptical Methodologist

Software, Rants and Management

No, You Don’t Need to F*cking Rewrite

I slummed it in /r/programming today and found this incredibly disheartening series of comments.

It was disheartening as it was just my birthday a few days ago, and I realized with this comment that I am now older than the average programmer.

Programming is a perpetually young industry, primarily because it’s still growing so fast. The need for more programmers means more and more folks are entering, keeping the average age low.

This is one of the reasons so many programming methodologies move in cycles – these kids have to re-learn everything the older folks learned.

Case in point: No, you don’t need to do a fucking rewrite.

The Rewrite Cycle

What everyone who thinks a rewrite is a good idea has failed to experience is when their own code gets rewritten. Let me give an example, here’s how kids seem to think the rewrite would work:

  1. The code is awful, full of TODOs and hard to understand. It got this way because “big, scary, bad management” made it that way. I hate “big, scary, bad management”!
  2. We should rewrite it rather than maintain it, this will save us in the long run.
  3. Woohoo! Now the code is clean, elegant and maintainable! Success!

But here’s how it actually fucking works, and why it upsets me every time this is suggested:

  1. The code is awful, full of TODOs and hard to understand. It got this way because humans are bad at coding.
  2. We should rewrite it rather than maintain it, this will save us in the long run.
  3. 6 Months Pass By
  4. Go to 1

It’s a cycle, not a process. And the reason code sucks, dear Brutus, is not with our management, but with ourselves.

What is Bad Code?

A lot of folks say bad code is code that’s ‘unreadable’ or ‘unmaintainable’ but that’s just a redefinition. What’s the real definition of bad/unreadable/unmaintainable code?

Bad code is code that you didn’t write.

Code only seems ‘elegant’ and ‘clean’ on a rewrite because you just rewrote it, and you had to wrap your head around the fucking problem such that now it all seems easy. You have inadvertently obtained the curse of knowledge. You saw something complex – at the heart of it was the fact that you didn’t understand the problem you were trying to solve. You mistakenly believed it had instead to do with the author’s choice of tabs or spaces, rather than realizing that you were just fucking dumb.

So you convinced some poor manager to do a rewrite, who had no idea how bad of an idea it was or didn’t know how to say no, or was afraid of you, and now you’re on the other side and the system you wrote seems ‘elegant’ and ‘simple’. But really, elegant is code for “I wrote this”.

What makes me so frustrated with this is that each generation of programmers has to learn this and watch idly by as the next generation, which is somehow so much smarter and better than the rest, makes the same goddamn mistake.

What to Do Instead?

Refactor, don’t rewrite.

“But this code is shit! I don’t want to do it! It’s complicated and it makes me feel stupid!”

How the code makes you feel is not a concern of mine. I feel stupid all the time, and I’ve come to realize that feeling stupid is the first step towards learning something. I’m not going to back your plan to rewrite a critical piece of our system because you can’t admit you don’t feel smart when you read it.

Code that’s been delivered has some of the roughest, best forms of testing there is – soak testing. Code that’s been delivered has some of the roughest, best forms of peer review there is – a bunch of really angry maintainers. Code that’s been around awhile has a lot of value.

Is it ugly? Sure, but that’s because everything of more complexity than a “Hello World” is ugly. It’s our responsibility as engineers to try and polish that turd as well as we can, and to do that we refactor, we increase test coverage, we run tools, and we do peer reviews. We dedicate some of our time to learning new design techniques and applying them judiciously.

“But shouldn’t we just ‘start from scratch’ and do all of it ‘right’ from the beginning?”

No! Goddamnit, No! That’s what the last person said. And the one before that. And it will be what the next 23-year-old, fresh out of a CS degree, says about your code.

Stop the cycle.

Refactor, don’t rewrite.



April 26, 2018 Posted by | Uncategorized | Leave a comment

SYWTLTC: Appendix A: Object Oriented Primer

There are whole books dedicated to this topic, and there are multiple chapters of SYWTLTC [to be] dedicated as well. But sometimes it is best to start at the beginning, so with Object Oriented programming, or OOP, that’s what we’ll do.

What is OOP in a nutshell?

Object-oriented programming is a ‘paradigm’, or style, of programming where programs are broken apart into separate ‘classes’ that interact.

These classes are ‘instantiated’, or created, at runtime to become ‘objects’.

In the following code, the object foo_int has class int

foo_int = 3

In the following code, the object foo_bar has class Bar_class.

foo_bar = Bar_class()

‘Variable’ and ‘object’ can be thought of synonymously. Similarly, ‘type’ and ‘class’ can also be thought of synonomously.

Classes can be thought of as ‘blueprints’ or ‘templates’ for objects. You can have as many integers as you want, but all of them behave similarly – behave like integers. This is because the integer ‘class’ is the same for all of them, they all come from the same template.

How do you Implement OOP in Python?

In Python, basic OO is done by using the class keyword. Python allows you to define classes, and then from those classes, instantiate (create) objects.

To define a class in Python named Foo_class, we do the following:

class Foo_class:

The pass keyword here means we’re skipping the implementation section of a class.

Instantiating Classes

To instantiate our class Foo_class, we call the class name with a parenthesis as if we were calling a function,

foo_object = Foo_class()

Once you have an instantiated class, also called an ‘object’, you can do things with it, like call a method.

We’ll continue to use this naming scheme – appending _class and _object and the like on everything to make it clearer what these things are. This is not required, and in actual code would be bad style as it’s overly wordy.


Methods are functions that are ‘attached’ to one of your objects. They’re like your normal functions, but they also pass a special argument called ‘self’ that is the data associated with your object.

Creating Methods

Methods are created on the class itself just like functions, except indented once to let Python know that the method belongs to the class.

class Foo_class:
    def bar_method(self):

Here pass tells Python we don’t want to implement our method yet.

Calling Methods

To actually use a method, you call it from an object rather than the class directly. We use the same notation for going inside a module, the dot (ex: . ) to go inside an object.

foo_object = Foo_class()
foo.bar_method() #calls the method bar_method, defined in Foo_class

Another example:


jerry_object = Jerry_class()
jerry_object.eat_sandwich_method() #tells Jerry to eat a sandwich by having the jerry_object object call the eat_sandwich_method method


Instance Variables

Objects can also have variables attached to them, using the special self argument. These variables are ‘state’ that is passed around from method call to method call as a way to share common variables between method calls.

class Foo_class:
    def set_bar_method(self, bar_argument):
        print("Inside set_bar")
        self._bar_instance_variable = bar_argument
    def print_bar_method(self):
        print("Inside print_bar")

In the above class, we define two methods, one method to set the variable _bar_instance_variable and another to print it. We can use it like the following:

foo_object = Foo_class()
foo_object.print_bar_method() #Prints '5' to the screen

This example illustrates some of the usefulness of objects – we’ve ‘bound’ state, in this case, we’ve set the state of the variable _bar_instance_variable to the value of 5. This state stays the same until we rebind or mutate it later. Our foo_object object will remember this state forever, and all future calls to print_bar_method will print 5 until we change it.

But there’s a subtle bug in the above code, what if we called print_bar_method before we called set_bar_method?

Special Methods

There are a variety of special methods in Python that the language uses internally. One special method, called __init__ is used to solve the problem above. This function is also called the ‘constructor’ or ‘initializer’. This is the ‘secret’ method that is called whenever you call a class to create an object.

foo_object = Foo_class() # initializer function is actually being called here

We can see this in action if we write our own initializer:

class Foo_class:
    def __init__(self):
        print("Inside Foo Init Special Method")

Now when we instantiate the class, we’ll have ‘Inside Foo Init’ printed to the screen:

foo_object = Foo_class() #prints 'Inside Foo Init' to the screen

Initializers can also be used to solve the problem we saw above, in this case, we can set _bar_instance_variable to a known safe default value. That way when we call ‘print_bar_method’ it will print the default value if it hasn’t been set.

class Foo_class:
    def __init__(self):
        print("Inside foo.__init__")
        self._bar_variable = 0 #set _bar to default value
    def set_bar_method(self, bar_argument):
        print("Inside set_bar_method")
        self._bar_instance_variable = bar_argument #reset bar to new value
    def print_bar_method(self):
        print("Inside print_bar_method")
        print(self._bar_instance_variable) # prints either 0 or our new value of bar if its been set

This class definition is safe to call print_bar_method and set_bar_method in any order. We can go one step further by actually taking the default value of _bar_instance_variable in the initializer’s argument list.

Initializers can take arguments like everything else, and you pass them through the class constructor parenthesis:

class Foo_class:
    def __init__(self, default_bar_argument):
        print("Inside foo.__init__")
        self._bar_instance_variable = default_bar_argument
... rest of the implementation is the same ...

And then to create an object of this Foo class, pass in the default value you want in its constructor:

foo_object = Foo_class(5) #creates a new Foo object with _bar set to 5

Note: Printing out where you are inside functions is called ‘tracing’ and is useful for learning, but shouldn’t be considered idiomatic or best practice.

What Problem does OOP Solve?

Wow, so that’s a lot of technical detail and for what? This doesn’t ‘do’ anything new like loops or if statements did. Why is this in the language?

Well, just like nearly everything after loops and if statements, OO is in languages to manage complexity. It provides another means of breaking down a larger problem.

A Brief History

In the before times, we didn’t even have loops and if statements. We had just one ‘control’ mechanism, the conditional jump.

Programs would be littered with ‘labels’ which were places you could jump to, and conditional jump commands were like supercharged if statements. They’d see if something was true, and if it was, they’d jump to their target point.

#pseudo code
beginning: #this is a label named 'beginning'
#this is a conditional jump, if x is 0, then we start executing at the label 'beginning'
if x == 0 then goto beginning

As you can see in the above, we’ve made ourselves a nice little infinite loop as we just keep jumping up to the beginning and then checking to see if x is 0, then jumping again.
In this era of conditional jumps, a ‘design pattern’ emerged called the ‘subroutine’. A design pattern, which we’ll get into in other chapters, is basically a piece of code design or form that is often repeated such that when people see the pattern, they tend to think of it in terms of the pattern rather than just the raw pieces.

A subroutine was basically a pattern of ‘set some variables, set a return jump point, then jump into some code’. It was a way to reuse code – people could copy and paste the subroutine code and reuse it in their own programs.

Goto considered harmful

A really smart guy wrote a pretty good essay about software design basically arguing that using ‘gotos’ (jumps) created what he called ‘Spaghetti Code’. Often in these programs, you couldn’t figure out the state of anything. You had no idea where folks were ‘jumping’ to you, what the state of the program was, or where you were ‘jumping’ to. The only way you could tell is by reading the entire code, which got really complex really quick.

Enter Structured Programming

Two paradigm shifts happened which we’ll both put under the moniker ‘structured’ programming.

First, ‘design patterns’ of using gotos in specific ways emerged. People noticed that sometimes they used a conditional jump to just execute one of two blocks of code depending on some variable, and then execution combined back into a single set of instructions.

if x != 0 goto else
#assuming x is 0 here
goto endif

In the above pseudocode, we add 1 to x if its 0 and 2 to x otherwise. This pattern showed up a lot, so people decided to put it into the language itself and call it an if statement

if x == 0:

Similar patterns were seen in ‘for’ and ‘while’ loops – constructs set up to goto the top of a set of code until something was true (we were through the array we were iterating over, or some other state had changed.)

Structural programming enthusiasts pointed out that despite how generic and powerful conditional jumps were, you really could throw them out and write all your programs using if statements and loops.

So goto was banished, and we got our ifs and loops.

Around the same time, the idea of the ‘subroutine’ was also codified, and ‘procedural’ programming was introduced. In this case, languages had special constructs to set up the subroutines in a known safe way – the jumps were handled by the programming language as were the arguments.

Structured programming gave us our ‘def’, ‘if’, ‘for’, ‘while’, ‘break’, ‘return’ and ‘continue’ keywords in Python. There were dark times before.

Next Up: Modules

Structured programming (if, for, while, break, continue) helped simplify programming into a smaller set of better-understood abstractions. Procedural programming (def, return) helped us reuse and share those smaller set of abstractions by giving chunks of understood code to pass between programmers.

Still, we had programmers basically copying down procedures they found in books or from their notes to ‘reuse’. Some procedures were hard-coded into the language themselves to make it easy to use them, but the idea of an abstract and reusable procedure still had a way to go. Eventually, raw text substitution through ‘include files’ were able to automate some of this tedium, but modular programming attempted to do even better.

The problem with procedures was ‘state’. Large, complex procedures – especially mathematical procedures – need to ‘remember’ things between calls. This was all done via ‘global variables’ that everyone could see and modify. Arguments were allowed, of course, you can have input and output arguments to talk to procedures. But the procedures themselves may still have some effect on global variables and you’d never know.

It was hard to reason about.

Modular programming attempted to solve this problem – it basically broke the global state into many much smaller globes. It also tried to solve the reuse problem – programmers could distribute already-written (and sometimes already-compiled) modules of their code which would have everything you needed to make calls in and out of the procedures you wanted to share. And with the module in place, only code inside the module could modify state inside the module.

Modular programming gave us ‘import’ and scoping rules (local versus global variables).

Whence Objects?

From our modular root in the 1960’s and 70’s, more patterns emerged. Just as modules attempted to put best practices of sharing procedures and hiding their implementation details, the widespread use of modular programming brought new patterns and idioms to light.

These procedures could hide global state, but even inside a module, sometimes you wanted to have two different versions of a procedure running. To accomplish this, you had to ‘pass’ all the state around everywhere. Smart languages like C allowed you to just copy pointers places, so this was able to be done efficiently. Still, code that was meant to be reused – and reused widely within the same program and thus can’t maintain just one set of state – needed to now bundle data structures with each of its functions.

An example of this might be a ‘moving average’ calculator that each time you call will update a moving average of the last five times you called it. Having this reusable is really useful, but having multiple moving averages in the same program is more useful still. You can’t have a single procedure with state since the moving averages would conflict with each other, so you have to have a procedure that will take in all the state it needs in its arguments.

Libraries of reusable procedures ended up looking more and more like the following:

def mathy_thing(data, arg1, arg2....)
def mathy_thing2(data, arg1, arg2...)
def mathy_thing3(data, arg1, arg2...)

All the ‘arg1’s may be different, but mathy_thing1 through 3 all share the same ‘data’ arg which has all the state they need.

Simultaneously, people were taking modules to the extreme. They’d make very small modules, and lots of them, to best split out their code. Inside modules, all the state was still shared, so if you wanted to isolate things from each other to ‘information hide’ and keep things simple, you just had to make more and more modules.

Finally, the influence of software design was looking at simulations. More and more large, complex programs were trying to simulate things in the real world like missiles, planes, accounts, and employees. Often it was easiest to think about all these little things as their own little-isolated modules.


Objects soon came about. Objects were thought of as tiny modules that could be instantiated multiple times with different state. It formalized the idea of passing the data to a module as its first argument to all of its functions (now called methods). Python still has this with the ‘self’ argument.

It put into the language the best practice of ‘setting up your state’ in its constructors and ‘tearing down your state’ in its destructors (not used heavily in Python). And it made it easy to make many dozens of very small ‘modular’ pieces of code.

Objects ultimately are just containers for functions, like modules are. But all of those functions have something in common – they all operate on the same shared data. In Python, this is called ‘self’. They are usually given ‘noun’ like names as a design principle, and their methods have ‘verb’ like names.

This pattern appears more and more the more complex your programs become. When you write a simple program, procedures (also called ‘functions’ in the more modern parlance) seem like overkill. After all, you’re just writing ‘hello world’ to the screen.
But after awhile, you begin to notice you’re having to type things over and over again, so you start to use procedures.

Likewise, as your programs get more complex, you begin to break your procedures up into different modules where they ‘feel like they belong’. You also notice that procedures often need some of the same inputs and outputs.
This is where you begin to introduce objects.

Alternatively, you can start with objects from the beginning, analyzing your domain for ‘nouns’ and ‘verbs’ and beginning to write classes from the get-go rather than refactoring later. This is what the simulation folks did, as they ended up using OOP to help them model their problem and understand it better.

What are some Basic OOP Principles?

There will be separate chapters on some more advanced OOP principles and approaches, but these are good ideas from the get-go to know.

Encapsulating State

Objects are there to ‘hide’ implementation details or ‘state’. One way to break down a problem is to pretend you’ve solved certain parts. Objects help you ‘pretend’ that by providing basically an interface or barrier through their methods. For example, let’s say you’re writing a payroll app but you really don’t know how 401k’s or payroll taxes work.
OOP allows you to just ‘hide’ that problem and come back to it later, meanwhile giving you a clean way to express it:

class Employee_class:
    def calculate_401k_method(self, contribution_argument):
    def calculate_payroll_taxes_method(self, tax_rate_argument):

In the above code, we haven’t actually solved any problem, though we have begun to design – we know that the ‘self’ container can hide all the implementation details of Employee we might need later. But for now, we can capture the idea that employee ought to be able to calculate it’s 401k and payroll, and that the idea of a contribution or tax rate is important.

By encapsulating state, we also have prevented outsiders from screwing things up. This helps us when we’re coding up procedures by knowing that our various variables are ‘protected’. Anything inside self can only be modified from another method we’re writing – if we open a file in one method and put it inside ‘self’, we should be confident that it is still open in another method.

We can make more assumptions about the ‘state’ of the system.

(Note: Python allows you to modify variables in ‘self’, but this is a pretty bad idea. Other languages are much more strict, using terms like ‘public’ and ‘private’ to define what’s protected.)

Interfaces and Inheritance

In C, a pre-OO language, people began adopting the pattern of ‘separating interface from implementation’. That is, they defined their functions twice – once to describe what their names were and what arguments they took, and another time to actually code up what they did.

This allowed people to further hide code implementation – so long as the number of arguments or name of the procedure didn’t change, someone could make more modifications to the actual code and no one else would have to change. It wouldn’t ‘leak out’.

For example, let’s say you’re using a geometry library, and it has a procedure to find intersections. Let’s also say that the maintainers of that library discover a bug in their code. So long as they don’t change the number of arguments or name of the intersection function, when they release the bug fix, you won’t have to change any of your code either. The implementation has been fixed out from under you.

Objects take this even further, allowing you to plug in a variety of objects if they all fit the same ‘interface’. By making this pattern so easy, it begins to infect the way you think about solving problems.

For instance, let’s say the way you do payroll taxes is different between exempt and non-exempt employees. So long as they both support the interface:

def calculate_payroll_taxes_method(self, tax_rate)

then you can intermingle the two objects all over the place. You can even store them in the same list and iterate over them, calling calculate_payroll_taxes_method on each one, none the wiser. When one method tends to change, usually other methods like 401k also changes. Objects make this easy to show the relationship between classes using ‘inheritance’.

Concrete Inheritance

Concrete inheritance tends to be used less nowadays, but it is used for the times when one object might be a specialization of another. Let’s say for instance that Exempt_class employee does all the same things as Non-Exempt employee except their 401k calculations isn’t the same. We can express that in python by having Exempt employees inherit from non-exempt employees.

class NonExempt_class:
    def calculate_payroll_taxes_method(self, tax_rate_argument):
        #implementation for non exempt
    def calculate_401k_method(self, contribution_argument):
        #implementation for non exempt

class Exempt_class(NonExempt_class):
    def calculate_401k_method(self, contribution_argument):
        #implementation for exempt

By not implementing the calculate_payroll_taxes_method for Exempt_class employees, we ‘fall back’ to the NonExempt_class implementation.

Meanwhile, by implementing the 401k method, we’re ‘overriding’ that method call in the case of Exempt employees.

x_object = NonExempt_class()
x_object.calculate_401k_method_method(contribution) #calls NonExempt.401k
x_object.calculate_payroll_taxes_method(tax_rate) #calls NonExempt.payroll_taxes

y_object = Exempt_class()
y_object.calculate_401k_method(contribution) #calls EXEMPT.401k
y_object.calculate_payroll_taxes_method(tax_rate) #but here it calls NONEXEMPT.payroll

Interface Inheritance

This is a more widely used technique nowadays. In interface inheritance, you don’t share code at all. You just make the promise that you have the same set of methods. Python supports this with what it calls an ‘Abstract Base Class’ which we’ll get into later. But you can also do something simply by adding a third class that both classes inherit from:

class Employee_class:
    def calculate_401k_method(self, contribution_argument):
        pass #always pass, this is a base class and this will never be implemented
    def calculate_payroll_taxes_method(self, tax_rate_argument):
        pass #always pass

class Exempt_clas(Employee_class):
.... do the Exempt implementation of our two methods ...

class NonExempt_class(Employee_class):
... do the NonExempt implementation of our two methods ...

Interfaces are very useful to ‘model’ your domain and set up hierarchical relationships. It helps break down the problem you’re trying to solve further into the ‘how’ interfaces and the ‘what’ implementations.

For instance, if you’re a supervisor and need to help a junior developer solve a problem, you might just give them a set of tests and an interface that they should write their code to. If they inherit from that interface properly, any code they give you should plug into the wider system without modification.

Cohesion and Coupling

This idea actually goes back to modular programming, but they’re two slightly antagonistic properties that are always attempting to be balanced.
Cohesion is basically ‘how much do these things belong together’? It says ‘all these functions take the same state, maybe I should group them even tighter together by putting them all as methods on an object.’

If methods don’t have much in common, they’re not ‘cohesive’.

Coupling is a similar idea, how much does a change in one part of the code affect a change in another part? If you have to change file1 every time file2 changes, then they are said to be ‘highly coupled’. We try and use procedural, structural and OO techniques to break out coupling. For instance, maybe we refactor out the common changes into file3 and have file1 and file2 refer to file3. All future changes will be made to one file, and file1 and file2 should change far less.


Live Coding Session

This comes from a series of tutorials and gives a live walk-through example of creating your own classes. Check out his other tutorials for other tips.

Code Reading

Flask is a python web framework that’s known to have good OO style. Check out this file for usages of methods, inheritance and multiple classes.

Code Challenge

Two greenfield exercises to upload into a single repo today.

These challenges are primarily about how to implement OO in Python rather than in-depth OO design.

See ‘N Say

First, we’re going to make a software model of a child’s “See ‘n Say”.

Create a “SeeNSay” class which has a “spin” method that will randomly print the following template to the screen:

“The [animal] says [animal_noise]”.

To do this, also create an Animal class, which will have the two methods ‘noise’ and ‘name’. Noise will return a string of what noise the animal makes, while ‘name’ will return a string of the animal’s name.

The animal class can ‘pass’ on these two methods, but you’ll need to have at least three subclasses such as pig, dog, and cow. Each of these will inherit from animal and fully implement the noise and name methods.

In your SeeNSay class, create a list of objects as an instance variable, each object instantiated from your animal classes respectively. Spin should use this list to randomly pick an animal and use its name and noise functions to print the right thing to the screen.

See here for a hint on picking something at random.

Remember to test and document. The animals should be easy to test since they’ll return their names and noises. The Animal class, being a base class, is merely a design element and does not require testing. The SeeNSay class prints to the screen so will be hard to test, however you should probably ‘smoke’ test it by including it in a single test and not making any assertions. This will ensure if the code crashes, you at least catch that in a test.

You can ignore any pylint warnings that say your classes may be too simple, but try to correct the rest.


This exercise is a little more free form. You’re going to make an object-oriented model of a car.

Create a car class, which will be subclassed how you see fit by things like “Truck”, “Sedan” or “Coup”.

What should be a car’s instance variables? Add things like “Model” and “Make” as strings, but also things like “Engine” and a list of “Wheels” as objects. The wheel and engine objects, what methods should they support? “Ignition” and “Stop” maybe for the engine, while “Turn” for the wheels?

What other objects can you add to a car? Should there be a subclassing scheme?

For the actual meat of these methods, just put in tracing print statements like “Inside Engine’s Ignition method”. Test to the extent you can, but since so much will just be printing side effects, you may just do one single smoke test that runs through your program to ensure nothing crashes.

For Mentors (And Coders Too)


  • Receiving a ‘guided tour’ via the debugger is important for both of these challenges since they’re largely structure oriented rather than behavior oriented.
  • Ask your mentee about the live coding sessions and code readings. What questions did they have? What did they find interesting?
  • Talk about a design you heavily used OO in and how it helped.

Review Checklist

  • Design:
    • OO Incohesive: Are there places where objects should not have been used?
    • Modular Cohesive: Do files/modules methods, classes and variables have to do with one another? Are they logically grouped?
    • OO Coupling: Are state variables highly reliant on each other (coupled) but not encapsulated in an object?
  • QA:
    • Is test coverage at 100%
    • Is it pylint clean 10/10?
    • Does the code use assertions?
    • Is pylint doc strings clean?
    • Is the documentation readable?
    • Does the code use good names?
    • Does the code use good use of white space?
    • Does the code have consistent and idiomatic style?
    • Does the code include comments?
    • Does the code use git hooks for pylint, pylint docs, and git commit lints?
    • Does the Readme explain what the code does and how to install and test the code?
    • Can the coder give a ‘guided tour’ using the debugger through one of their test cases?

January 30, 2018 Posted by | Uncategorized | Leave a comment


There are two P’s of work: productivity and prioritization. All too often, we focus on productivity: how can I do more with the same amount of inputs? Or, since productivity is so closely linked with quality, we ask: am I building the thing right?

More rarely, we ask: am I building the right thing? This question is answered by how we prioritize.

Most People’s Prioritization Strategies is “Don’t”

Most people don’t prioritize. Or, if they think they do, they abide by some overly naive protestant prioritization scheme that work matters more than fun or prioritizing studying over partying.

We’re not talking about work versus fun. Welcome to adulthood.

We’re talking about actually prioritizing the work that’s coming in, and when it comes to that most people fail to prioritize altogether.

When faced with multiple projects that have to get done, folks routinely fall into the scheme that all projects are equally important, and all projects must get done.

Usually, this is where they turn, wrongly, to questions of productivity. They need to do X, Y, and Z and can’t seem to get it done. Maybe there’s a more efficient way to do it such that they can do X, Y, and Z?

Or, far more likely, they fail to become more productive and simply work more hours.

If anyone is routinely working more than they should, they probably have a prioritization problem.

The Most Important Prioritization Question

Prioritization schemes abound, but they all attempt to answer the same question. This question causes ghastly shrieks among those naive protestant masses who simply put more and more and more effort into projects and refuse to believe a prioritization problem exists.

What can drop on the floor?

When people say “It all has to get done!” they’re refusing the difficult work of prioritizing. More importantly, they’re setting themselves up for a slow decline. By simply throwing more hours at a problem, they’re going to lower their productivity, and even worse, lower their quality. Eventually, they won’t even be able to do what they used to do, and they’re going to lose revenue because of it.

Doing everything is not an option.

“But,” you cry, exasperated, “I really do have to do everything! Everything is important! Each one of my projects is tied to a key client!”

If that’s what you’re thinking, then you’ve utterly failed to figure out what “key client” means. You can only ultimately juggle so much work. Clients taken on above that level are, by definition, not key. Revenue taken on above a level of sustainability can never be ‘key’ revenue.

Since you can’t decide which clients are key, which revenue is key, you may think that you’ve rendered them all key, but in fact, none of your clients are key.

None of your clients will achieve a level of service above any other, and none of your revenue will be protected above any other. None of it is key. To say something is key, to prioritize it, means that you will sacrifice something else to save it. That’s what it means for something to be key.

Once you figure out what can be dropped on the floor? You’re ready to prioritize.

“Wait!” you cry some more, “My projects have deadlines!!!

Then figure out what deadline you can miss. This question isn’t any different than what client you can drop – you have to focus on the things you can achieve or risk losing them all. If you don’t focus, you will wear yourself out, and drop all the balls. If you focus, you will continue to juggle the ones you focus on.

Two Strategic Prioritization Strategies

Okay, so you’re ready to prioritize. In fact, you’re at a strategic level, and you want the folks below you to prioritize. How do you do that?

The Embedded Customer

One means of enforcing priorities is to embed the high priority client with a development team itself. That way new work always comes through that customer, rendering it impossible for one customer to walk all over another customer’s project by priority conflict.

What teams get embedded customers can be reorganized once a quarter or once a year. It’s best, if using this approach, to make a ‘diversified portfolio’, with your portfolio ‘weightings’ determined by how important various customers are. Large customers with a lot of growth potential may get 60% of your teams, while difficult customers may only get 10% of your teams.

The Shared Vision

Another approach is to do the difficult job of prioritizing what the company ought to work on once a quarter or once a year, then communicate these priorities out to the teams. The teams are then empowered to chase these priorities as they see fit.

Often this is called a ‘strategy’ and it’s decided at a high level. Most often what we see in terms of ‘strategy’ isn’t so much a strategy at all as much as promotional puffery and overly abstract grand statements. When you realize that a lack of a strategy, or a bad strategy, is ultimately the same problem as a lack of priorities, or bad priorities, you shall be enlightened.

Strategies ought to be concrete and simple, with no more than 3-4 priorities and their ordering. While each priority needs to be broad enough to allow for interpretation by your different teams, they also need to be concrete enough to avoid dumb abstractions like “Our strategy is to do what it takes” or “Our strategy is to focus on business success”

I mean, really, those aren’t strategies. Strategies are decisions in an environment of tradeoffs (sounds like prioritization to me!). If your strategy does not inform the rest of the company of the trade-offs you intend to pursue – what you intend to focus on and what you intend to let drop – then your strategy is not a strategy.

You also need to make education a part of this vision – your teams need to have the knowledge and the tools to know whether their ideas successfully implement your strategy or not. If you’re asking your engineers to focus on a certain kind of client but not giving them the marketing know how to know who that client is, your strategy will fail.

The Combination

These two approaches can be combined, with an overarching strategy feeding into the portfolio mix – if your strategy is largely to go after one segment of customers rather than another, you can embed representatives of those customers on more of your teams.

The shared vision can then pushed to those teams as a focus point, and help guild what projects they come up with beyond simply chasing the latest and greatest thing that client may want.

You Have to Focus and Drop Something

Making more widgets per hour isn’t going to help you if the market is no longer buying widgets. You need to step back and think about what you actually have the resources to do (namely, time) and from there, decide what steps you’re going to take.

Attempting to do everything is a cliche model-student way to react to this problem. When the teacher throws more work at you, you often learn to party less, to even sleep less, and nail those grades. Well, no one’s actually looking out for you now like a teacher might – you literally are going to have to figure out what class you plan to fail to ensure you pass your others. This isn’t a lesson many people get in school.

Enforcing prioritization on a larger scale must be done too, and often crosses over into what’s called strategy. Embedding goal makers (client representatives) into teams is one way to streamline this, and then how you weight the teams becomes your prioritization problem. Alternatively, giving your teams a short list of large goals that they can independently figure out how to achieve can also eliminate priority conflicts.

Ultimately though, priorities and strategy are about trade-offs. And to make a trade-off, you have to let something go.

January 15, 2018 Posted by | Uncategorized | 1 Comment

Two Management Patterns

These tactics have come up in the past few days. They could be called patterns, or they could be called antidotes to various antipatterns.

First, Do No Harm: The Hippocratic Pattern

Hippocrates was a Greek physician and one of the first medical thinkers. Modern day doctors take the Hippocratic Oath, which begins “First, Do No Harm”.

What the oath tries to stress is that many options you have available to you can implicitly make the problem worse. By taking action, you’re taking risk. Often either due to optimism bias, wishful thinking or just panic mode, we take actions that we don’t fully think through the risks.

Does this mean never take action? No. If it meant that, doctors would never treat patients. What it means is that all action should be weighed against its pros and cons and that taking action often comes with risks – especially in a stable system – that go undervalued.

Loss Aversion: Or, Why the Feature You Have is Always Worth More than the Feature You Don’t

One big reason tech debt piles up is because we refuse to turn off features that don’t pay the rent. These features usually fall into one of two camps – either a feature that never really took off with heavy use in the market or a feature that was once popular but has since been eclipsed by something else.

Features are complexity. A product that supports ‘more’ features will, all other things being equal, always be more complex than a product that supports ‘fewer’ features.

These features are the source of revenue, and if the revenue doesn’t cover the costs of maintaining them, they need to be cut. This is usually a very hard decision due to a concept called ‘Loss Aversion’.

Basically, loss aversion is when it hurts you more to lose something than it would cause you pleasure to gain it. The best example can be seen in an experiment on coffee mugs. Scientists asked folks to rate how much they’d pay for a coffee mug. They, on average, put the price of the coffee mug at $2. That implies (if we’re in a perfectly rational world) that giving the coffee mug to a person would give them $2 worth of pleasure.

You can measure the same thing by giving them the coffee mug, and then offering to buy the mug from them. Again, in a rational world, they should be willing to take the $2 in exchange for the mug – the two things, $2 and a mug, should be fungible and of equal value. But they aren’t.

Folks wouldn’t give up the coffee mug, once they had it, for $2, but instead usually asked for $3 or more. The pain of losing the mug was worth more to them than the pleasure of gaining the mug.

Knowing this story can help you frame your argument, should you have to argue that a feature ought to be discontinued. Instead of saying “Feature X only gets us Z many customers, and costs us Y to maintain, we should cut it”, simply turning the phrase into “There’s a feature out there that’d cost Y to build and get us Z customers, should we do it?”

When folks say no, that there are better projects to chase down, then you mention “Well actually, this is a feature we already have. I’m wondering if its slowing us down from getting more valuable things.”

Avoid the idea of loss – focus on what you can gain (the opportunity cost of keeping the outdated feature are high, frame them in terms of new things you’re passing up), and also get your colleagues to commit to a rational belief that we shouldn’t pay Y dollars for Z customers before the reveal that you’re already doing so.

November 16, 2017 Posted by | Uncategorized | Leave a comment

SYWTLTC: (AB) Chapter 3.5 Type Checking

This is the final chapter in our software quality series.

We’re going to draw on object-oriented techniques below, so make sure you’re this far along in Python Programming:

  1. Programming Basics (ch 1, ch 2)
  2. Beginner Data Structures (ch 5, ch 11)
  3. Structured Programming (ch 7, 8)
  4. Procedural Programming (ch 6)
  5. Algorithms and Recursion (ch 13)
  6. Object Oriented Programming and Design (ch 4, ch 10, ch 9, ch 12)
  7. Numerical computing (ch 3)

Let’s recap.

We’ve talked about testing – that’s a tool you write yourself that verifies parts of your program.

We’ve talked about linting – that’s a tool someone else has written that will analyze your code for common errors.

We’ve talked about contracts and assertions – or the idea that our functions can and should make promises to each other, and if those promises are violated, our program should crash. This, in a way, embeds a program inside your program so that it can check itself.

Finally, we’ve talked about peer review and collaboration – this is the only non-programmatic means we’ve introduced to ensure quality. Have another programmer read over your code.

There are interesting crossovers and reinforcements.

  • You shouldn’t test your tests, so all you have are linters and peer review on test code.
  • Littering assertions and contracts through your code means every test you run checks many more things, so they build on each other.
  • Assertions can document assumptions, making peer reviews easier.
  • Linting can leave code more consistent, making peer reviews easier.
  • Much more…

There’s one more technique that is not as popular in Python but very dominant in other languages and this one technique, when applied well, can prove the absence of certain kinds of errors.

Eye of Newt, Hair of dog…

What would happen if we combined our linter with our contracts? In other words, what if we could have something check that our code never violated our contracts?

x = 3
assert x > 4

We want something that will flag the assertion above as false without running the code. Something that can reason about our code statically, and discover errors automatically.

Enter the Type Checker

First, a little random history.

Back in the early 1900’s, a philosophy known as Logical Positivism was having its prime. Logical Positivism claimed that logical sentences – sentences constructed via a specific mathematical process – made either true or false claims about the world. Sentences that violated the mathematical formulation were determined to be gibberish.

It was an attempt to place the entirety of human knowledge on the basis of mathematics. And at the very center, was a mathematics called Set Theory.

Sets are more or less just lists or collections of things. The set of sheep, for example, or the set of prime numbers. Primarily used for number theoretic questions, Set Theory – with enough hammering – could in and of itself define basic arithmetic (called Peano arithmetic), and thus begin to define the rest of mathematics.

There was a problem though – a huge hole in the middle of set theory that leads to a paradox. Anything can be a set, after all. What about this – is this a set?

“The set of all sets that do not contain themselves”

A set is a collection – certainly, it can be a collection of collections. So that checks out, and nothing in Set Theory says the sets can’t be self-referential – either containing or not containing themselves. So that checks out too. Seems like it’s a set.

Let’s call the above set X. And we’ll ask a very easy question that blows up all of Set Theory – does X contain itself?

If X is inside X – X contains itself – then by definition, it can’t – because it belongs to the set of all things that do not contain themselves.

Of course, if X is not inside X, i.e., X does not contain itself… then it does contain itself since it’s the set of all things that do not contain themselves!

Logical Positivists wanted all logical sentences to be True or False – not paradoxical. Thus began the great quest to figure out how to strengthen set theory to once again be sound enough to be a foundation for all mathematics, and thusly all human knowledge.

Two interesting things happened out of this – one, a weird offshoot called Type Theory came to be. The other interesting thing that happened was the Incompleteness Theorem which more or less said the whole quest was doomed from the start. No matter what you do to Set Theory – if it’s powerful enough to create Peano arithmetic, it will always contain paradoxes.

You can think about that second part for a while and the futility of ever organizing human knowledge or thinking that mathematics was a sound and complete system of reasoning. We’re going to talk about the first more minor blip.

What type of thing is a type?

Type Theory tried to categorize ‘things’ that can be in sets into different ‘types’ which can’t be compared. A set is one type, a set of sets is another type, and a set of sets containing itself would be a violation of type theory – since from one angle you’re talking about a set, and from another, you’re talking about a set of sets.

You know what? Let’s go back to the Incompleteness Theorem, cause it shows up again here of all places. While Gödel was working on the Incompleteness Theorem, another smart dude named Alan Turing was coming up with his Halting Problem.

See, the problem was he wanted to see if a program could be written to determine if another arbitrary program crashed. It’s tricky reasoning he used, but similar enough to the ‘sets that don’t contain themselves’ mind bender above. Basically, he proved that it was impossible. No program could determine if another arbitrary program ever stopped or went into an infinite loop.

Your linter will never detect whether your program crashes or not. It can detect certain kinds of crashes, based on patterns. But it can’t rule everything out. It’s mathematically impossible.

In fact, a lot of problems in computer science have been determined impossible by proxy. If you can show that by solving your problem X, you could solve the halting problem, you know that solving X must be impossible.

The idealized debugger is one of those programs. We’d love a program that could inspect ours, find all the bugs and fix them, without any human intervention. Unfortunately, one problem the idealized debugger could fix is infinite loops, and thus it’d solve the halting problem. The idealized debugger is impossible.

Back to types. In an effort to ensure types weren’t “too powerful” as to allow paradoxes so that they could properly constrain set theory, mathematicians invented a type system that a computer could implement. In other words, by reasoning about types, a computer could prove an absence of type errors and only type errors.

How did they do this? Types aren’t as powerful as sets. They’re restrained. You cannot implement Peano Arithmetic in types, and from a computability standpoint, strict type systems aren’t ‘Turing complete’. They’re a constrained form of programming that you can put on top of your less constrained program to borrow its safety when you need it.

What’s a type error? A type is a ‘category’ of thing. So in the case of Python, trying to add a number to a string is a type error. Numbers can’t be added to strings. Taking the square root of a string is similar – square roots expect a type of number, and so, it’s impossible.

Dynamic versus Static Typing

Python is a dynamic language though – in an effort to allow a little more power, Python doesn’t type check until runtime.  This makes type checking only as powerful as assertions/contracts. We know assertions and contracts are great to have in code, but they cannot guarantee the lack of certain kinds of errors – they only help to debug them when they happen in the wild.

There are other kinds of languages out there that use a ‘static’ type system – this type system is enforced and checked before the program is even run. Like a linter, the type checker is a program that runs over the code itself. More powerful than a linter, instead of merely looking for patterns in the code, the type checker actually interprets the code and builds a model of the code in memory. It then ‘proves’ various things mathematically about that model, such as the absence of type errors.

There’s further categorization – so-called strongly typed languages versus weakly typed languages. This basically is a measure of how much you’re allowed to break the type system. Languages like C are statically typed, but also weakly typed. This is due to the fact that there is a type checker, but you break it at any time by doing what’s called a void pointer cast. You have a string? You can trick C into thinking it’s a number pretty easy.

Python is a dynamic and reasonably strongly typed language. It won’t allow you to break the type system, but it doesn’t enforce type errors until runtime.

Haskell is a static and very strong type system. Haskell has many ways to reason about types such that if you model your program via types well, and it compiles, you have proven out a lot of bugs.

A New Fighter has Entered The Arena!

So if Python is dynamically typed… why talk about type checking?

Because the above is no longer the case. There are now type checkers for Python. We’re going to look at this one.


How to Annotate

The first thing you’d want to do on code that isn’t type annotated, or on new code, is add the types. Python already supports type annotations but just ignores them. So you can add annotations in the style of here to your code here.

The best bang for your buck will be annotating function signatures. I’ll tell you why in just a bit.

How to Run

Run mypy like a linter. Instructions are here.

How to Add New Types

We’re going to be doing basic Object Oriented programming in the code challenge, which you should be familiar with from Python Programming.

Check out how MyPy automatically turns all of your classes into types, respecting inheritance here.

Type Checking Versus Other Methods

Certainly, you can see how type checking is related to linting and assertions. It’s basically a combination of the two, solving a certain kind of problem.

It’s more powerful than a linter as it actually uses a proof engine to reason about your code and more powerful than assertions as it can rule things out statically, rather than just triggering dynamically.

It cannot find all the little things a linter can though, so the two should be combined. And most of your program needs to reason dynamically – not statically. This means that types cannot model your entire system, and you should fall back to assertions when you have to.

Types serve as a powerful form of documentation, enhancing peer reviews. They make code easier to reason about by assigning each variable a certain type. They also clean up variable names, as

def func(string_first_name):

is always going to seem less readable than this

def func(first_name : String):

How does type checking compare to testing? This is where things get interesting.

Unit Versus Integration Tests

There are two large classes of tests – what’s called unit, and what’s called integration. There are other kinds of tests, but these types of tests are most often written.

Unit tests are supposed to test a small bit of code in isolation, quickly. Dependencies like a database or file reading are ‘mocked out’ using special code that pretends to be a database or a file.

Integration tests put multiple pieces of code together, as well as third-party dependencies like databases. They tend to be slower and exercise much more code. They are also often more difficult to write.

Unit tests often chase ‘coverage’ – trying to get each line of your code run by at least one test. When attempting to increase coverage, unit tests are usually the easiest thing to spin up and write more of. A coverage of 70% is pretty good, 100% is the highest you can go.

There’s a goal in mind.

Integration tests try to test integration points, which can get hairy. Let’s say you have three components you use (a website, a database, and a script). We’ll call them X, Y, and Z. You’d need to write…

…an integration test of X to Y…

…an integration test of X to Z…

…an integration test of Y to Z…

…and an integration test of X to Y and Z.

four integration tests to test 3 components. Conversely, if you had three well-factored components and needed to unit test them, you’d only need to write… three unit tests. Unit tests scale linearly with the number of components you want to test, while integration tests scale with the size of the superset of all components. Which is bigger than linear.

With 4 components you’d have 11 integration tests you’d need to write, but only 4 unit tests.

It gets out of hand quickly, and often, no one writes that many integration tests. Unit tests are easier to write. So there’s this ‘black hole of integration’. Most people write a few integration tests – usually never enough.

Types fill the Black Hole of Integration

Types, especially types on function signatures, are promises along with a ‘boundary’. Function signatures are often the integration points between components. If you used a database, what you’d really do is use a database library, and call a function in it.

That function is where you want to put your types. This function – the gateway to the database – is the ‘boundary’.

Each integration point can be decorated easily with types. If you have component X and Y and Z, it’s a linear effort to add types to component X, then Y, then Z. You do not need to add types just for X talking to Y, or X talking to Z. It’s like unit tests.

The type checker can then generate all of your integration checks for you, ensuring that whenever X talks to Y, they’re talking in the same language. They’re using the same types.

Type checking can turn the overwhelming integration test problem into something that’s pretty easy to manage. Don’t test integration points, type check them.

Typeful Programming

Often you’ll see detractors of type checking argue that the number of times they’ve confused a ‘float’ type for a ‘string’ type is next to none. It’s dumb to have a check for something that never happens.

And they’re right – the built-in types of the language rarely conflict. Simply decorating your code with ‘string’ and ‘integer’ and the rest isn’t going to suddenly discover a lot of bugs, nor is it going to reduce the risk of introducing new ones.

The power of types in programming is realizing they’re a tool that you can use too. Integers and Strings are what the programming language designers wanted – you can create your own types and use the type checker to enforce it.

What types do you want? This is important from a design perspective in object-oriented programming, which more or less asks – “Pretend you already have the objects (types) you need to solve the problem, then write that program.”

If your program talks in terms of temperature, you’d better not have floats running around. You should have a Fahrenheit type and a Celsius type. Those types can be implemented-in-terms-of floats but should be represented in your code as fully fledged types.

This makes it impossible to do silly things like adding a zip code to a temperature, and possible to do useful things like automatic conversions between temperature types.

A heuristic here, especially since it takes years of trying to get a good intuition around object-oriented design, is looking for ‘primitive’ types and get rid of them. If you’re passing an ‘int’ or a ‘string’ – ask yourself. Are you really passing an int or a string? Or are you passing a count of vegetables and a name of a vegetable? If you have those things – and you don’t have a type defined in your code-named ‘Vegetable’ – add it and refactor!

Let’s take the following program for example:

age = get_raw_input("Please enter your age")
print("You are {0} years old.".format(age))

The age variable above is an integer. But is it really? No. It’s AN AGE!

Ages are represented by numbers, but only certain numbers. And they’re a concept we can think a lot about.

Consider adding the following class to the above program:

class Age(object):
    def __init__(self, raw_age):
        assert raw_age < 124,\ 
            "You can't be older than the oldest person alive!" 
        assert raw_age > 0,\ 
            "You can't be less than 0!"
        self._raw_age = raw_age

    def input_age(): -> Age
        return Age(get_raw_input("Please enter your age"))

    def print_age(self): -> None
        assert self._rage_age is not None
        print("You are {0} years old.".format(age))

The above program is a little longer – typeful programming like the above requires a little more overhead in small programs. But you see we’ve modeled a concept – Age – in our program, and it makes the program easier to reason about. We’ve now got ideas like enforcing a range on age. Human ages don’t go to a billion, and if one age in your program was at a billion, that probably means there’s a bug somewhere.

In large programs, typeful programming is actually far shorter. This is because you’ve built a large dictionary of ideas and concepts to build more advanced concepts from. The number of assertions and tests you need to write will shrink because you’ll be reusing all the assertions and tests you’ve written on all your small classes/types. And you’ll need far less defensive coding and integration tests since you can use the type checker to enforce most of the integration points.


Live Coding Sessions to Watch

Remember, when watching coding sessions don’t just look at the main content being covered, but watch what tools the coder uses. Look for typing techniques, where they lay their screens out, what plugins they might use on their editors, and so forth. Research things you find interesting, and try to incorporate them into your workflow!

The two below are a bit more ‘produced’ than I usually prefer, but keep in mind that real programmers make mistakes all the time, have to look things up, and so on.

Below is a quick live session that uses MyPy

To round this out, here’s another coding session out of left field – and introduction to the Web Framework “Flask”. This is a 7 part series if you’re interested, but for now, just get through the first introduction.

Code Reading / Review

For the reading, let’s look at MyPy itself – you’ll be looking at its options loader here.

Practice doing code reviews, what comments would you leave for this code? Think of these following questions to discuss with your mentor:

  1. Where did comments, style, and docstrings help or hinder your reading of the code?
  2. How much of the code could you loosely understand? Why or why not?
  3. How much did types help you understand the code?
  4. What did you like about the code? How might you replicate that in your own code?
  5. What did you not like about the code? How would you avoid those practices in your own code?

Code Challenge

You’ll be refactoring some old code that handles geometry lookups, or ‘geo-fencing’. It’s a prototype to see if a cell phone’s latitude and longitude falls within some boundary. The problem is, there’s a bug in it, and no one can figure out what it is.

Your first mission is to add at least two new classes/types: Latitude and Longitude. After creating these classes and removing the old floating point numbers that represented latitude and longitude, see if that along with MyPy doesn’t help you figure out where the bug is.

Finally, with the bug fixed, push test coverage up, clean up pylint, add assertions and prep the code for review.


When exploring code you inherited, use some of the other tools in your toolbox. We like to get tests in place first, but often it’s hard to figure out what the code is even doing, much less how to test it.

Try running the debugger on the code and stepping through it line by line – see what variables are changing. What are those variables supposed to be?

Feel free to add comments as you go – inherited code is not always well documented. You can make notes and annotations as you attempt to understand the code.

Feel free to change variable names or make formatting changes too – it may be best to get some pylint flags cleaned up first that deal with style. This can make ugly inherited code easier on the eyes.

Add assertions where they make sense – if you think the code works in a certain way, is there a way you can assert that? For instance “I think X and Y should be equal here… maybe I should add an assert that they are.” Assertions are your assumptions about code – if you add them to the code itself, you allow the program to check your assumptions.

As you understand the code more and more, you can take some larger steps to try to refactor it out into something testable.

The real lesson here is not to leave code this ugly for others to inherit! Remember to leave tests for others, maintain a sense of style with the linter, and over document! I’m giving you ugly code on purpose to help remind you why good-looking code matters. It’s a huge productivity benefit during maintenance, and maintenance is 90% of where you’ll spend your time coding.

For Mentors (And Coders Too)

Mentors – Ask your mentee about the live coding sessions and code readings. What questions did they have? What did they find interesting?

Review Checklist

  • Are types documented and MyPy clean?
  • Is test coverage at 100%
  • Is it pylint clean 10/10?
  • Does the code use assertions?
  • Is pylint doc strings clean?
  • Is the documentation readable?
  • Does the code use good names?
  • Does the code use good use of white space?
  • Does the code have consistent and idiomatic style?
  • Does the code include comments?
  • Does the code use git hooks for pylint, pylint docs, and git commit lints?
  • Does the Readme explain what the code does and how to install and test the code?
  • Can the coder give a ‘guided tour’ using the debugger through one of their test cases?

May 9, 2017 Posted by | Uncategorized | Leave a comment