Similar to how it seems that many programmers play a musical instrument, particularly the guitar, it seems that many programmers love to play chess. Now that may be the case only because people in general like to play the guitar and play chess. But I think programmers especially like playing the guitar because it allows them to express their creativity, and they like playing chess because of their proclivity toward analytical problem solving.
There aren't many problems out there that are more complex and difficult than a game of chess (with Go possibly being one of them). Yet, programming is full of mind-bending problems that we have to overcome everyday, and so there are many parallels between chess and programming. We could certainly learn some things about programming from the strategies involved in playing and getting better at chess.
Phases
For starters, chess has three phases of the game: the opening, the middle game, and the ending. In the opening, both players follow a series of moves that have been played time and time again in thousands of previous games throughout history. Most of the advantages or disadvantages of each move have been studied extensively, and there are accepted lines of play for each side that will lead to slight advantages to one player or the other. If the players have studied openings extensively, the opening may last for twenty moves or more. Similarly in programming, when you start a new project, you will go through a known set of steps to setup the environment and lay the foundation for the rest of the project. Much of the initial setup will build a skeleton for the rest of the code to hang from. It gives the project structure, much like the opening moves in chess will build up a pawn structure to use as the pieces are developed and put into motion.
At some point one of the players will introduce a novelty - a move that takes the game out of established opening play - so that the players now have to rely on their own chess playing skills. This phase is the middle game, where strategy and tactics take over as each player grapples for advantages and attempts to maintain them through the rest of the game for a win. The middle game can be fraught with uncertainty, with countless options available on each move and few certain paths to victory. Tactical strikes can change the balance of the game quickly. The advantage can quickly change hands as attacks succeed or fail and pieces are exchanged.
The long middle game of a software project can have its fair share of uncertainty as well. Every design decision entails tradeoffs and compromises, and the final success of the project is largely unknown, much less what it will actually look like once all is said and done. There will be days where everything seems to be going great, other days where nothing seems to work at all, and that momentum can change in an instant. This is the time to celebrate small victories, attempt to keep morale up, and strive to maintain momentum and positive progress.
Back in the chess game: at some point enough pieces will have been removed from the board, and the position becomes more clear. The transition from the middle game to the ending is not as well defined as from the opening to the middle game, but at some point it becomes obvious that the end of the game is imminent. In this phase every move becomes critical, and the game can be won or lost because of a single pawn move at the wrong time or the precise path that the king takes across the board. Precision of execution is paramount. The same goes for software projects. At some point most of the planned features will have been added, and the bugs are getting progressively cleaned out of the system. It becomes clear that this software will actually ship, but the execution from here on out is critical. Everything has to line up precisely for the best possible outcome on release day, and the slightest mistake could make a huge difference in the success of the product.
Pattern Matching
Okay, the phases of the game showed some similarities to software projects in broad strokes, but there are a number of other more specific things that we can learn from the game of chess. At its heart, chess is a game of pattern matching, and that is how the masters are so good at playing. When they look at a chess board, they don't see two sets of sixteen separate pieces on individual black and white squares. They see combinations of interacting pieces controlling space on the board. They see a pawn structure that defines the game and shows which strategies will be appropriate to pursue. They can instantly memorize any given position and assess the strengths and weaknesses by recognizing common configurations of pieces so that they can spend their mental energy analyzing the best course of action.
If you've ever watched an expert programmer at work, you may be amazed at how quickly he (or she) can determine how a piece of code works and make necessary changes. This ability comes from the same pattern matching skills as the chess master, but the patterns are in the code. I'm not talking only about design patterns here. An expert programmer stops seeing individual keywords, variables, and operators, and instead he sees the structure of the code itself. He can recognize similar blocks of code that he's seen in the past and work with it at a higher level than just reading and manipulating individual symbols.
For the chess master, this pattern matching ability is useful beyond merely assessing a position. Being able to recognize more chess patterns will give you a better ability to calculate the best lines of play, and it will improve your horizon - how many moves ahead you can see. Strengthening this ability to concentrate on the position and calculate deeply in spite of what pressures the player is under is known in chess as developing mental toughness. That concept is perfectly appropriate to programming. As you gain more experience and expand your ability to see software patterns, you will be able to see larger and more complex transformations of code with less mental effort. You will be able to anticipate more potential problems and envision more promising solutions when writing code, all by improving your mental toughness.
To develop this skill in chess, you should do lots and lots of practice drills. There are entire books filled with positions where one player has an immediate tactical advantage over the other, and you are supposed to see the correct lines of play that capitalize on that advantage. You can also do all kinds of different drills with a few pieces to practice essential tactics like pinning, forking, skewering, and discovered attacks. Seeing these tactics will become more automatic as your pattern matching skills improve. There are also all kinds of ways to improve your pattern matching skills in programming. In particular, studying algorithms and data structures, and practicing on programming puzzle websites like Project Euler or Programming Praxis will greatly improve these skills.
Strategy And Tactics
Much of master level chess revolves around the tension between strategy and tactics. Strategy involves the long term advantage you can leverage from things like the pawn structure and the control of space and critical squares on the board. Tactics instead consist of the short term execution of maneuvers that result in a decisive material advantage. Skewering your opponent's king to trade a rook for a queen or forking the king and bishop with your knight could lead to an overwhelming advantage of force, if you can hold on to it.
The opening is where strategies are first put into place, and most of the opening theory is based on gaining a strategic advantage, assuming the opening lines are played correctly by both players. The middle game is the land of tactics, and this is partly why the balance of the game can change so quickly during this phase. Most novice players make the mistake of focusing too heavily on memorizing different openings, when they should really be studying tactics. Even though strategy depends on deep chess knowledge, most games at the amateur and even the master level are primarily determined by tactics.
When amateurs look at grandmaster games, they will read an awful lot of discussion about subtle positional advantages and strategic play. What amateurs seem to forget is that to reach that level of play, grandmasters had to be absolutely excellent at tactics. They have gotten so good at tactics that for the most part, they have taken tactics out of the game. At least, that's what it looks like on the surface. If you look at the alternate lines of play in a grandmaster game, they are littered with devastating tactics narrowly avoided on both sides. The point is that grandmasters are exceptionally skilled in both strategy and tactics, no matter whether they are described as positional players or tacticians.
In programming creating new code can be thought of as strategic play and working with legacy code can be equated with tactical play. When writing new code, you must think longer term about all of the implications of the design you implement. How you structure the code could have an impact on the project for years down the road because it establishes a way of doing something that can become hard to change as more code is added to the original architecture. On the other hand, working with legacy code entails making surgical changes to existing code, and small changes can be decisive in fixing broken code or adding new features. Refactoring in particular can be thought of as a known set of tactics that can improve legacy code with small, well-defined code changes.
As with strategic or tactical grandmasters, expert programmers may have a preference of creating new code or working with legacy code, but they are excellent at both tasks. The defining characteristic of experts is not whether they are better at building something new or fixing something old, but that they have exceedingly good judgement about programming in general. Just as a grandmaster will have a better idea of when to develop her position and when to go on the attack, an expert programmer will know when it is better to write their own code or use a library and what the best way to debug a particular piece of code would be. Experts have a general sense of good judgement that comes from years of practice, experience, and hard work developing all of their skills. Their preferences for one particular way of doing things tend to show only when the trade-offs of choosing one way or the other are insignificant.
Beauty
It only takes a short time learning chess before you begin to realize that the game is filled with beauty. You can hardly find any book on chess that doesn't mention it in one way or another, and commentary on famous chess games is filled with acknowledgements of beautiful positions or exchanges or checkmates. The more you learn about the game, the more you can appreciate the beauty that unfolds from the interaction of the pieces and how they can work together when the game is played well. One of my favorite types of checkmates is the pure mate, where every square around the mated king is either attacked or occupied only once. Being able to achieve something so perfect in the course of a game with nearly endless possibilities certainly elevates the game to a work of art in the hands of the masters.
The best programmers strive for beauty in code as well. For them it is not enough to merely create functional, productive software. They strive to transcend normal, everyday code to create masterpieces that solve complex problems in clear and elegant ways. For them programming is not a job or a hobby, but a labor of love that they pursue with a deep passion. Of all the things chess can teach us about programming, this one becomes the most obvious with time. Bringing order to complexity is a thing of beauty that is deeply satisfying to achieve.
No comments:
Post a Comment