From 231abb9858e13800d44e2a15bb2218b122756a20 Mon Sep 17 00:00:00 2001 From: Craig Maloney Date: Fri, 5 Jul 2019 23:54:54 -0400 Subject: [PATCH] More changes for ch. 3 (revision control) --- chapter03.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/chapter03.md b/chapter03.md index 8e90568..6ff6f6d 100644 --- a/chapter03.md +++ b/chapter03.md @@ -2,17 +2,17 @@ ## Whoops! -It's bound to happen: something you thought was a good idea didn't work the way you planned and now you realize you've made a terrible error. Sometimes it's something that could have been easily avoided (committing code that was meant for debugging, for instance). Sometimes it's a cascade of errors, each building on the efforts on the previous error. There's the mistake of neglecting the side-effects of a module when it's used in a way that wasn't intended. Or it's the realization that you've designed a small, tightly coupled module only to learn that your module will be part of a larger piece of software and your code isn't designed to make a smooth transition. Whoops! +It's bound to happen: something you thought was a good idea didn't work the way you planned and now you realize you've made a terrible error. Sometimes it's something that could have been easily avoided (committing code that was meant for debugging, for instance). Sometimes it's a cascade of errors, each building on the efforts on the previous error. There's the mistake of neglecting the side-effects of a module when it's used in a way that wasn't intended, or it's the realization that you've designed a small, tightly coupled module only to learn that your module will be part of a larger piece of software and your code isn't designed to make a smooth transition. Whoops! The mistakes that really frighten me though are the ones that I did not expect; the ones where the unintended consequences run rampant throughout the system like a chain-reaction. Those mistakes keep me up at night. Programmers make mistakes. The nature of our jobs require us to be aware of what is going on in multiple sections of code. We lose track of the state of our program and committed code. We try to pepper our code with comments and reminders of what's going on in a section of code but comments become stale and add to our distraction. We rush and rely on muscle memory to pick up the slack. We deny ourselves areas where we can adequately test code because we feel we need to get things done sooner. -We panic and when we panic we make mistakes. +We panic, and when we panic we make mistakes. ## Avoiding mistakes -Let's be clear: there's no way to avoid or eliminate mistakes. Software is too complex to be completely bug-free. But what we can do is create places where we can tease out as many bugs from the code as possible before we set it in front of others. We can better understand our code and what it's doing when we have the ability to debug and test our code in a safe environment. We can see how it will behave under certain circumstances. Creating a model of the target system allows us to test our code against miniaturized versions of the target system's reality and see how it behaves under those conditions. +Let's be clear: there's no way to avoid or eliminate mistakes. Software is too complex to be completely bug-freer, but what we can do is create places where we can tease out as many bugs from the code as possible before we set it in front of others. We can better understand our code and what it's doing when we have the ability to debug and test our code in a safe environment. We can see how it will behave under certain circumstances. Creating a model of the target system allows us to test our code against miniaturized versions of the target system's reality and see how it behaves under those conditions. We put a lot of emphasis on avoiding mistakes, both in programming and in programming culture. There are horror stories of how small bugs in a program caused enormous pain for those involved. The moral of these stories illuminate that simple mistakes can be costly and we need to be doubly careful about avoiding mistakes. These anecdotes are told in the hopes that they'll somehow scare developers into being more cautious, but they can have the opposite effect. They can make programmers paranoid about making any mistakes at all, and when we operate in a fear-based mode we begin to panic. Telling programmers to make no mistakes is similar to telling someone not to be afraid: they become more afraid of being afraid. @@ -22,25 +22,25 @@ The best (and perhaps only) way we learn is by making mistakes. Learning by maki We need environments where programmers can safely learn from their mistakes. We need spaces where programmers can feel good and confident about trying new things. We need places where developers can try out their ideas and not have those changes ripple out to other unrelated systems. This is the best way that developers can learn and be brave in their learning process. -These environments must model the target systems, and they must be as close as is practical to those target systems. That doesn't mean you need to make exact copies of expensive production environments but you do need to create models of production environments that test most of the pieces your code will come in contact. Having models that mirror production systems means that when you move your code to production you'll introduce fewer changes with unintended consequences. Your changes will have already existed in a production-like environment. You can take comfort in knowing that the changes you enact in these models will be the same changes that will appear on the target system. +These environments must model the target systems, and they must be as close as is practical to those target systems. That doesn't mean you need to make exact copies of expensive production environments, but you do need to create models of production environments that test most of the pieces with which your code will come in contact. Having models that mirror production systems means that when you move your code to production you'll introduce fewer changes with unintended consequences. Your changes will have already existed in a production-like environment. You can take comfort in knowing that the changes you enact in these models will be the same changes that will appear on the target system. Ideally you'll need to have an environment like this on a machine that you control. This means that you're not competing with other programmers in your organization who are also being brave with their changes. But you'll also want to ensure that you keep your environment up-to-date with their changes (and any production changes) so your development model matches what's on the target system and what will be on the target system. A good model is one that is kept current with what it is modeling. It's the same as a map of a city: it's best when it matches the area its modeling and is kept current with changes that occur in that city. A good map of the city might tell you about the recent construction happening on your route. A useless map doesn't even show your route because it wasn't built when the map was created. If our model of production is constantly falling behind what's in production we will spend more time rectifying the changes that we're making with the changes between our model and production. -This also means having an environment that you can rebuild quickly and replicate as needed. Having a model that becomes its own separate reality becomes one more system to maintain. This model should be something that you can delete and rebuild at will in order to remove any previous experiments. It's best to think of it as an ephemeral copy of your target environment that has limited use and can be tossed when no longer necessary. It should be quick to replicate this environment so there's little friction in creating new environments to play in. That can mean scripting the building process for these environments. How you decide to do this is up to you but keep in mind that you want something that's as simple as you can make it and requires as little thought as you can manage to replicate it. +This also means having an environment that you can rebuild quickly and replicate as needed. Having a model that becomes its own separate reality becomes one more system to maintain. This model should be something that you can delete and rebuild at will in order to remove any previous experiments. It's best to think of it as an ephemeral copy of your target environment that has limited use and can be tossed when no longer necessary. It should be quick to replicate this environment so there's little friction in creating new environments to play in. That can mean scripting the building process for these environments. How you decide to do this is up to you but keep in mind that you want something that's as simple as you can make it and requires as little thought as you can manage to replicate it. -Again, it doesn't have to be perfect - it's only a model. But it does need to be close enough where your code will behave in a similar fashion between the model and the target environment. +Again, it doesn't have to be perfect - it's only a model, but it does need to be close enough where your code will behave in a similar fashion between the model and the target environment. ## Time machines -There are plenty of other folks who will tell you the benefits of revision control (and many folks who will show you the exact steps for how to set up a revision control system). Revision control systems such as git, svn, cvs, and the like have helped programmers coordinate releases and keep a log of what work was added to their project. Having a good revision control system allows you to create areas where you can test code without having to merge these tests into production code. Good revision control lets you to create a space (or "branch" in git parlance) based on existing code that you can use to experiment and develop. It also allows you to commit in that space and diverge as much as you want or need to in order to fully explore the changes you're making. What's most important though is that good revision control will also allow you to abandon that space if you need to - you're not forced to add those changes back to your production code. This allows you to see if something might work and abandon those changes if they don't pan out. Good revision control affords the programmers the ability to branch off from any point in time and explore what happened in the code base. In a sense they're time machines and infinite universes, allowing you to play "what if?" scenarios with your code and move back and forward through time in your code. This is vital for your learning because you can feel secure in testing and trying things and have the ability to rewind those changes (or delete them entirely) without affecting the work of others. +There are plenty of other folks who will tell you the benefits of revision control (and many folks who will show you the exact steps for how to set up a revision control system). Revision control systems such as `git`, `svn`, `cvs`, and the like have helped programmers coordinate releases and keep a log of what work was added to their project. Having a good revision control system allows you to create areas where you can test code without having to merge these tests into production code. Good revision control lets you to create a space (or "branch" in `git` parlance) based on existing code that you can use to experiment and develop. It also allows you to commit in that space and diverge as much as you want or need to in order to fully explore the changes you're making. What's most important though is that good revision control will also allow you to abandon that space if you need to - you're not forced to add those changes back to your production code. This allows you to see if something might work and abandon those changes if they don't pan out. Good revision control affords the programmers the ability to branch off from any point in time and explore what happened in the code base. In a sense they're time machines and infinite universes, allowing you to play "what if?" scenarios with your code and move back and forward through time in your code. This is vital for your learning because you can feel secure in testing and trying things and are able to rewind those changes (or delete them entirely) without affecting the work of others. -Learning how your revision control system works will give you freedom in making mistakes. Many of these systems can seem complex at first but with continued practice and patience you'll understand what the revision control is doing and what its capabilities are. You'll be able to judge how many risks you can take with your code and be more confident with the risks you take. +Learning how your revision control system works will give you freedom to make mistakes. Many of these systems can seem complex at first, but with continued practice and patience you'll understand what the revision control system is doing and what its capabilities are. You'll be able to judge how many risks you can take with your code and be more confident with the risks you take. -Revision control can also play a role in seeing the development of the code of other folks. You can get a window into their development process and see what certain features look like as they are added. This can help you learn about an unfamiliar code base, and can show you the direction they took in order to make the code the way that it is. It can give you a window into the history of a project and what all went into making it happen. It can be a time machine into the history of a project and can help you understand that programming is a process. Not all projects come fully-formed from programmer minds. +Revision control can also play a role in seeing the development of the code of other folks. You can get a window into their development process and see what certain features look like as they are added. This can help you learn about an unfamiliar code base and can show you the direction they took in order to make the code the way that it is. It can give you a window into the history of a project and what all went into making it happen. Revision control can be a time machine into the history of a project and can help you understand that programming is a process. Not all projects come fully-formed from programmer minds. ## Learning from failure -Sometimes we fail. Sometimes the code that we wrote isn't up to the realities of the system it's implemented on. We push code that does something unexpected and systems break as a result. We can lose track of where we are in our code and make changes that conflict with other changes and cause us to spend the rest of the afternoon undoing those changes. In all of these cases it causes discomfort, whether to us, the folks we support, or the folks we work with. +Sometimes we fail. Sometimes the code we write isn't up to the realities of the system it's implemented on. We push code that does something unexpected and systems break as a result. We can lose track of where we are in our code and make changes that conflict with other changes and causes us to spend the rest of the afternoon undoing those changes. All of these cases it causes discomfort, whether to us, the folks we support, or the folks we work with. I'm not going to lie: failure sucks. It makes us feel like we're less of a person because we failed. We feel inadequate and wonder how others think of us. Do they think less of us? Have we damaged our relationship with those who use whatever we've programmed? Have we let our team down? All of these questions stem from two desires: a desire to do our best and desire to do no harm to others. We want others to think well of us and our skills. Failure runs counter to those desires and amplifies whatever feelings of inadequacy we might have. Those feelings can include wondering if we should be programming at all or wondering if our talents should be used elsewhere. We wonder if we should just give up. @@ -54,7 +54,7 @@ Mistakes also act as a reminder to pause for a moment and not get too wound up i ## Journaling our mistakes -There's value in not making the same mistakes twice. Knowing that we repeated the same failure is useful because it gives us a pattern to understand. We can see that doing this particular thing gives us a failing result. We can then see what caused us to do the same thing and plan for how to mitigate it. This can be part of the learning process as long as we don't fall into a spiral of self-recrimination when we realize that we've made the same mistake again. +There's value in not making the same mistakes twice. Knowing that we repeated the same failure is useful because it gives us a pattern to understand. We can see that doing this particular thing gives us a failing result. We can then see what caused us to do the same thing and plan for how to mitigate it. This can be part of the learning process as long as we don't fall into a spiral of self-recrimination when we realize that we've made the same mistake again. One trick that I use more infrequently than I would like is journaling. Keeping a journal of what happened and how we fixed it is one way to explain to someone else (often ourselves) about what happened. Explaining what happened allows us to become a teacher to ourselves and others. It reinforces our learning process. Writing down what happened in a way that others can understand allows us to arrange the thoughts in our head in a way that is clear and understandable. When we articulate our own thoughts about what happened and codify them we start to understand our own thoughts and can shake loose other ideas about how to fix this and other problems. We give ourselves the pause we need to fully understand what happened and how best to move forward. We become our own sounding-board for ideas on how best to proceed. -- 2.31.1