PDF Summary:Coders at Work, by Peter Seibel
Book Summary: Learn the key points in minutes.
Below is a preview of the Shortform book summary of Coders at Work by Peter Seibel. Read the full comprehensive summary at Shortform.
1-Page PDF Summary of Coders at Work
A lively look into the software engineering mindset, Coders at Work by Peter Seibel shares insights and methodologies from notable programmers. Through informal Q&As, these developers reveal their problem-solving strategies, collaborative approaches, and philosophies behind writing maintainable code.
The interviewees—including Jamie Zawinski, Douglas Crockford, and Joshua Bloch—discuss finding the right balance between perfection and pragmatism, managing complexity as systems evolve, and questioning long-held assumptions. Whether you're an experienced programmer or new to the field, their diverse perspectives offer guidance on creating adaptable, scalable software.
(continued)...
Upon encountering an assertion, a program verifies the validity of the stated condition. If the specific condition is unfulfilled, an exception is triggered, causing an immediate halt to the program's execution. By swiftly identifying issues, developers can avert the progression of these concerns into more complex and formidable challenges. Bloch argues that using assertions is essential to document the required conditions and guidelines that developers must adhere to in order to preserve the fundamental correctness of the code's underlying logic during updates.
Donald Knuth emphasizes the importance of crafting code that is easy to understand rather than concentrating exclusively on strict formal verification.
Knuth acknowledges the value of formal correctness proofs in certain contexts, yet typically he leans towards a pragmatic approach, as noted by Seibel. He advocates for software that is inherently organized and crafted in such a way that developers feel assured of what their code is meant to do, thus obviating the need for a formal mathematical proof. He emphasizes the necessity of developing code that is clear and expressive, suggesting that this transparency is crucial for ensuring precision.
Knuth recognizes that although comprehensive verification can enhance confidence in the dependability of software, he believes that applying such rigorous standards to complex systems like TeX could become excessively burdensome and might even have negative consequences. He understands the importance of thorough verification, especially in the realm of exact computational formulas, where it is possible to definitively determine the initial conditions, the expected outcome, and the consistent attributes. He believes that for complex and large-scale systems, developing code that is both transparent and well-organized, along with thorough testing and debugging, leads to greater efficiency.
L. Peter Deutsch emphasizes the importance of advanced techniques for developing and ensuring program architecture to improve their dependability.
Deutsch, who has experience at MIT, Xerox PARC, and Sun, explores how thorough verification might improve software excellence, as Seibel emphasized. During his doctoral research, he maintains that accurately representing the complexities of real software systems is an exceptionally difficult task. He expressed to Seibel his conviction that the path to improved reliability lies not in assertions or inductive reasoning, but in creating deeper and more powerful forms of expression that are declarative in nature.
He argues that modern programming languages do not adequately communicate the intrinsic structure and intent embedded within the software. He argues that by employing more declarative programming languages, developers could concentrate on defining their overarching goals, while the intricacies of execution are managed independently by the system's compiler and runtime environment. As a result, this might pave the way for improved automated verification techniques that could significantly improve software integrity. He recognizes the difficulties involved in developing languages that offer a substantial degree of expressiveness while also allowing for integration at more abstract levels.
The Boundaries of Examination
This section explores the inherent limitations of testing as a method for ensuring software quality, recognizing that while testing is crucial, it can never guarantee complete correctness.
Jamie Zawinski was aware of the significance of swift development, while also acknowledging the need to balance product quality with time limitations.
Zawinski emphasized the importance of quickly evolving and releasing software. He places a higher value on delivering a functional product to the market swiftly rather than allowing extensive testing to postpone the release. He believes that although testing is valuable, it should not delay the launch of a working product.
Zawinski acknowledges that accelerating the creation of software can lead to particular defects that require later correction. He considers the equilibrium between the caliber of work and the rapidity of its completion to be crucial, particularly in sectors where market leadership can frequently dictate the chances of triumph. He believes that it is better to introduce a functional product with some flaws rather than postponing its launch while striving for perfection.
Donald Knuth employed a meticulous and stringent method to pinpoint inaccuracies in TeX.
Seibel emphasizes how Knuth meticulously scrutinized TeX, showcasing this as a prime example of using an adversarial approach to detect flaws. When Knuth made the initial release of TeX, he offered a reward of $2.56 for every bug that users could find in the program. He doubled the reward every subsequent year, eventually stopping when the reward had reached $327.68. Seibel's innovative strategy led to a collective of programmers conducting an in-depth analysis of TeX, which culminated in the discovery of many small mistakes.
The errors found within TeX highlight the difficulties of developing perfect software and emphasize the importance of thorough testing, even when there is a strong emphasis on structure and accuracy. Donald Knuth's unwavering commitment to identifying and documenting every mistake in TeX underscores his belief that errors are an inherent aspect of the job, and it is the responsibility of programmers to actively search for and rectify these errors, recognizing that they demand prompt resolution.
Peter Norvig emphasizes the significance of employing diverse testing techniques, recognizing that conventional boolean tests do not capture the full scope of software capabilities.
Seibel notes that Norvig acknowledges the insufficiency of conventional testing approaches, which typically assess binary outcomes, in fully accounting for the diverse behaviors software may display. Norvig champions the development of a wider array of evaluation techniques that go beyond simple pass/fail results, underscoring the significance of comprehensive and nuanced evaluations in practical environments for software systems.
Merely stating that the search outcomes produced by Google's engine are precise does not adequately evaluate the system. An improved assessment would necessitate taking into account aspects such as importance, prioritization, and the variety of potential outcomes, by examining an extensive array of results across various dimensions instead of merely determining a binary answer. Norvig emphasized the necessity to move beyond simple yes-or-no evaluations, highlighting the imperative to enhance software testing to thoroughly understand the characteristics that set apart superior software and the techniques for precisely evaluating those characteristics.
Other Perspectives
- While Jamie Zawinski's methodical strategy is valuable, it may not be as effective in a fast-paced development environment where quick iterations are prioritized over meticulous examination.
- Ken Thompson's use of print statements, although useful, can be less efficient than using modern debugging tools that provide more context and automation for identifying issues.
- Bernie Cosell's reliance on deep system understanding for debugging is important, but it may not be scalable or transferable to other team members who may not share the same level of knowledge.
- Joshua Bloch's advocacy for assertions assumes that programmers can anticipate conditions worth asserting; however, assertions can be misused or overused, leading to code clutter and false confidence in the code's correctness.
- Donald Knuth's preference for understandable code over formal verification might overlook the benefits that formal methods can bring to ensuring the correctness of critical systems, where failure can have severe consequences.
- L. Peter Deutsch's emphasis on advanced techniques and declarative programming for ensuring dependability may not acknowledge the current need for imperative programming in performance-critical applications.
- Jamie Zawinski's focus on swift development could compromise long-term maintainability and stability of the software, potentially leading to technical debt.
- Donald Knuth's meticulous method of debugging TeX with bounties might not be practical for all projects, especially those with limited resources or those that require rapid development cycles.
- Peter Norvig's call for diverse testing techniques beyond boolean tests may not address the challenge of creating such tests, which can be complex and resource-intensive to implement and maintain.
The advancement and improvement of programming, viewed as a creative endeavor, includes wisdom from eminent figures like Knuth.
This section explores the progressive development of programming, recognizing it as a relatively new endeavor for humanity, which continues to be honed for skilled practice.
Understanding the significance of analyzing code is essential.
This passage underscores the necessity for programmers to thoroughly examine and comprehend code, whether self-authored or created by colleagues, in order to improve their skills.
Jamie Zawinski honed his skills through the study of documentation and analysis of existing code rather than through formal education.
Zawinski underscores the importance of understanding programming principles by critically examining and assessing existing code, as noted by Seibel. He elucidates that his self-education was predominantly through the analysis of existing software code, complemented by the study of numerous books and manuals. Zawinski favors a practical, self-guided method over a structured educational program.
He stressed the importance of asking questions, even if it means revealing gaps in one's own understanding. Zawinski scrutinizes the programming intricacies to understand how a specific segment functions to meet a predetermined goal. He advocates for the "random walking" technique, which entails starting at a captivating point and systematically exploring the code to thoroughly understand the underlying mechanisms.
Brad Fitzpatrick honed his skills by interacting with code developed by his colleagues, identifying common design patterns, and drawing knowledge from experienced coders.
Fitzpatrick, who is mostly self-educated, was initially reluctant to engage with software developed by others, as Seibel observes. He eventually understood that such fear was unfounded. He encourages developers to immerse themselves thoroughly in programming, aiming not just to understand the functionality of a particular application but to investigate different structural patterns, uncover new approaches to problem-solving, and integrate the collective wisdom of the programming community.
Fitzpatrick recommends initiating work on a substantial codebase by making a minor adjustment, like modifying the application's title bar, which helps in becoming familiar with the construction procedure, comprehending the software's structure, and building confidence in managing the code. He also recommends starting conversations with experienced coders by making initial contributions to open-source projects, regardless of any potential imperfections in those contributions. This conversation, as highlighted by Fitzpatrick, offers beginners the chance to gain insights and guidance from experts deeply knowledgeable about the complexities of creating software.
Simon Peyton Jones stresses the significance of understanding the fundamental principles related to programming and recommends that absorbing concepts by reading can be advantageous for future use.
Seibel emphasizes the crucial contribution of Peyton Jones to the development of Haskell and its Glasgow compiler, highlighting the importance of understanding the core principle that supports a piece of code when analyzing it. He contends that by examining code, one can understand not just its functional components but also the foundational concepts and design tactics that are adaptable to a range of circumstances.
He offers an intriguing illustration of how indolence influenced Haskell's development. Lazy evaluation, initially regarded as an ingenious approach, became a fundamental concept that transformed the approach Haskell programmers take when crafting and constructing their applications.
The Impact of Programming Language Architecture on Development Methodologies
This section delves into how the tools of our profession, especially the programming languages we employ, shape our approach and thought patterns during coding activities, recognizing their significant effect on the way we conduct our work.
Brendan Eich aimed to advance dynamic programming languages by incorporating new capabilities to bolster the development of large-scale software systems.
Eich, the creator of Javascript, believes that the progression of language design is crucial to adapt to the changing needs of programmers, as Seibel has noted. JavaScript was initially designed as a simple language for small-scale scripting in web browsers; however, the increasing utilization of Ajax and the escalating need for sophisticated browser-based application development have driven the advancement of features that cater to comprehensive programming tasks.
Eich supported the ECMAScript 4 proposal, now shelved, which sought to introduce improvements designed to streamline the execution of common tasks, enhance conciseness, and minimize the chance of errors without expanding the fundamental computational functions of the language. He recognizes the significance of straightforwardness and the essence of concise language rules, yet he suggests that if a language is crafted exclusively for a select few and neglects the needs of the majority of programmers, it is probably doomed to be unsuccessful. Languages must evolve, he argues, incorporating methods that enhance efficiency, security, and ease of use, even if it leads to greater complexity in the process of creating software.
L. Peter Deutsch voiced his criticism of pointers, emphasizing their role in hindering the development of advanced programming languages and the production of reliable software.
Deutsch, drawing from his deep background in developing language processors for Lisp and Smalltalk, markedly downplays the role of pointers, as Seibel notes, stating his lack of knowledge of any technique that could improve software quality which depends greatly on that concept. He believes that the utilization of memory addresses and data links can create an intricate web of interconnected data structures, which can greatly increase the complexity for developers trying to comprehend the code, regardless of whether the programs are single-threaded.
He argues that this opacity hinders the understanding of how large-scale programs operate, particularly concerning the ownership of data, the sharing of information, and the control of access rights. He suggests a programming language structured to enhance efficiency would clearly separate the elements that concentrate on values from those managing sharing and referencing, thus offering stronger guarantees about data flow within a program and identifying the code areas that could engage with different data structures.
Joe Armstrong advocates for a messaging-based methodology, emphasizing its role in simplifying and clarifying the management of concurrent operations within Erlang.
Joe Armstrong, the creator of Erlang, advocates for employing a communication method where tasks exchange information to efficiently handle concurrent operations, as noted by Seibel. Erlang emphasizes a concurrency approach where isolated processes each hold unique data and communicate by exchanging asynchronous messages. Armstrong is of the opinion that adopting this method leads to the creation of systems that are not only less complex but also stronger, since it eliminates the need for complex systems to manage shared data structures and improves the ability to scale as the number of processors increases.
He believes that the focus on message passing within Erlang has led to a language design that naturally encourages modularity and the reuse of code elements. Erlang programs are designed for inter-process communication, which facilitates their adaptation and reuse in diverse contexts. Furthermore, since all inter-process communication happens via asynchronous messages, Erlang code naturally avoids many of the thorny problems associated with multi-threaded programming, such as race conditions or deadlocks.
In the domain of software development, mathematical concepts, structured approaches, and conceptual models hold considerable importance.
This section of the text explores the concept that understanding theoretical concepts can provide programmers with a deeper insight into the problems they encounter, and although formal methods might not always be directly used, they can aid in creating more effective solutions.
In his influential book, Donald Knuth emphasizes the significance of utilizing theoretical principles to tackle practical computational problems.
Knuth values the beauty and resilience found in mathematical principles, while Seibel highlights the necessity of applying these abstract notions to tackle real-world challenges. He illustrates this concept with a straightforward case involving the summation of numerous values. An alternative algorithm that capitalizes on the commutative and associative properties of addition will offer greater advantages for addressing the same issue on a parallel computer, particularly when contrasted with direct iterative methods suitable for smaller quantities.
Knuth delves into the complexities involved in generating reports in the field of software engineering. String concatenation follows the associative property, akin to numerical addition; yet, unlike number addition, it does not allow for the interchangeability of elements, making certain parallel techniques that are applicable to arithmetic unsuitable for creating reports. In his work, he illustrates that a profound grasp of fundamental theoretical principles equips developers with the capabilities to make superior design choices, select appropriate instruments for their work, and create robust, high-performing applications.
Guy Steele advocates for the adaptation of programming languages in response to the changing requirements of their users.
Steele, known for his critical contributions to the creation of languages like Scheme and Fortress, emphasizes the importance of considering how programming languages have developed, an aspect underscored by Seibel. He believes that the traditional view of language design as a complete, once-and-done process is no longer sufficient, particularly as programming languages grow more complex.
Steele argues that the design of a language should facilitate straightforward alterations, personalizations, and advancements to address new challenges and needs. He underscores the remarkable capability of the Lisp macro system that enables developers to significantly and logically expand the language. Steele advocates for a systematic approach to the development of programming languages, emphasizing a well-defined process to incorporate new features and relying on the insights of regular users to ensure the language remains effective and stable.
Fran Allen's conviction regarding the importance of conceptual formalization underscores the link between real-world challenges and abstract comprehension.
Frances Allen devoted her professional life at IBM to improving compiler performance and addressing the complexities of parallelizing program execution, and she is of the opinion that the progress in computer science is contingent upon the formalization of concepts, a point noted by Seibel. She believes that by converting intricate theoretical concepts related to practical problems into mathematical formulas, algorithmic methods, or novel methods of data structuring, one not only deepens their understanding of the matters but also illuminates the approaches to address them.
Allen underscores the significance of a reciprocal link between practical involvement and conceptual comprehension. Practitioners derive understanding from tackling practical issues, which can guide the creation of innovative theoretical frameworks, while theorists may offer sophisticated and potentially impactful methodologies that practitioners can apply and assess in actual systems.
Other Perspectives
- While the wisdom of figures like Knuth is invaluable, it's also important to recognize the contributions of lesser-known programmers and the collective wisdom of the programming community at large.
- Analyzing code is crucial, but hands-on experience in creating new code and solving novel problems is equally important for skill development.
- Jamie Zawinski's approach to learning through documentation and existing code is one path, but formal education can also provide a structured and comprehensive foundation in computer science theory and practice.
- Brad Fitzpatrick's method of learning from colleagues' code is beneficial, but it may not always be possible in environments where code is proprietary or when working in isolation.
- Simon Peyton Jones emphasizes fundamental principles, but practical, hands-on experience can sometimes lead to intuitive knowledge that is just as valuable as theoretical understanding.
- The architecture of programming languages indeed shapes development methodologies, but the creativity and adaptability of programmers can often overcome the limitations imposed by a language.
- Brendan Eich's work on advancing dynamic programming languages is commendable, but there is also a need to balance the introduction of new features with maintaining simplicity and ease of understanding for newcomers.
- L. Peter Deutsch criticizes pointers, but they can be powerful tools in the hands of experienced programmers for certain types of low-level programming and performance optimization.
- Joe Armstrong's advocacy for a messaging-based methodology is powerful in the context of Erlang, but other concurrency models may be more suitable for certain problems and domains.
- Mathematical concepts and structured approaches are important, but there is also value in experimental and heuristic methods that may lead to practical solutions when formal methods are too complex or inapplicable.
- Donald Knuth's emphasis on theoretical principles is foundational, but in some cases, rapid prototyping and iterative development can lead to successful solutions without deep theoretical underpinnings.
- Guy Steele's call for adaptable programming languages is important, but stability and consistency in language design are also crucial to avoid fragmentation and maintain a stable base for long-term projects.
- Fran Allen's focus on conceptual formalization is key to advancing computer science, but empirical approaches and practical feedback are also necessary to ensure that theories are validated and applicable in real-world scenarios.
The Inherent Complexity and Challenges of Large-Scale Software Development, and Approaches to Address Them
This section explores the difficulties faced by software developers as they are charged with the creation, maintenance, and improvement of large-scale software systems, and it also examines the diverse approaches they utilize to manage these complexities.
Creating systems with adaptability and scalability in mind.
This section of the book delves into the approaches these programmers employ to ensure their code is crafted with scalability and flexibility in mind, recognizing that software often requires updates and must adapt to changing demands and standards.
During his time at Netscape, Jamie Zawinski's experience highlighted the risks associated with succumbing to the temptation of an overly ambitious sequel, underscoring the importance of avoiding the complete overhaul of the system.
Zawinski highlights the dangers inherent in what Frederick Brooks refers to as "the sophomore system effort's pitfalls," a concept documented by Seibel. In their quest to develop a system that surpasses its predecessor in sophistication and perfection, programmers and designers often rely on lessons from the first iteration, leading to the creation of a follow-up version that is excessively intricate and delicate, making it susceptible to breakdowns. He believes that developers should be aware of their tendency and resist the urge to start from scratch.
Zawinski's tenure at Netscape exemplifies this phenomenon strikingly. The overhaul of the Netscape Navigator browser was driven by the need to evolve from chaotic coding practices to an organized, object-oriented framework, developed using C++. In striving for technical superiority, the team's focus shifted away from their main objective of developing a working browser. The revamp failed to yield a functional browser promptly, which allowed Microsoft to recapture the market lead that Netscape had once eroded, thereby contributing to Netscape's downfall and underscoring, as Seibel notes, the idea that the pursuit of perfection can result in failure in a competitive marketplace.
Douglas Crockford strongly advocates for simplifying and minimizing complexity within large, ever-evolving systems.
Crockford's extensive background in creating complex systems, including his work on a networked platform for Electric Communities and his contributions to web-oriented architectures at Yahoo, has endowed him with a profound grasp of complex problem-solving, as Seibel observes. He believes that it is essential for programmers and the companies they work for to develop methods for handling the increasing complexity of software systems, a complexity that grows as these systems evolve and expand. Crockford advocates for the exclusive use of language features, including APIs and established design methodologies, that have proven their worth, while avoiding components that offer little benefit.
He views the challenge of intricacy as transcending mere technical aspects, also involving societal and financial factors. He argues that the setting of software development has resulted in the emergence of complex and intertwined systems that often grapple with insufficient specifications, poor implementation, and are laden with legacy problems, making their upkeep difficult and presenting significant obstacles to their progress. Crockford champions the importance of simplicity and clarity, integrating insights gained from past errors to build systems that are flexible enough to meet changing needs and unforeseen obstacles.
Peter Deutsch prefers Python to Smalltalk because it promotes clear modularity and explicit dependencies, thereby improving maintainability.
Deutsch, an experienced developer with a reputation for contributing to major systems such as Interlisp, the Smalltalk virtual machine, and Ghostscript, emphasizes that the methodology for developing large-scale software must be distinct from that used for smaller projects, concentrating on modularity, maintainability, and the ability to evolve, as Seibel points out. Deutsch recognizes that Smalltalk is adept at rapidly developing prototypes and experimental applications, yet he views Python as the better option for building large-scale systems because of its emphasis on simple modular architecture and easy-to-use import statements.
He considers the way Smalltalk programming merges a program's operational state with its code, along with inherent dependencies, to be a substantial obstacle for the creation and maintenance of large-scale, collaborative software projects. In his view, Python's clear delineation of elements and its uncomplicated method for module incorporation lead to a more transparent and methodical strategy for managing dependencies and distributing code among team members, culminating in a workflow that is more structured, repeatable, and demonstrably precise within the domain of software development.
The shift expanded the focus from mere programming to encompass all aspects of software development.
The author explores the evolution of programming, which now encompasses a wider range of skills and responsibilities beyond just writing and debugging software.
L. Peter Deutsch conveyed concerns regarding the label "coder," emphasizing the broad spectrum of skills necessary for producing worthwhile software.
Deutsch, who shifted his career from software development to composing music, believes that the label "programmer" narrows the scope to just one aspect of the intricate process of developing robust software systems, a point noted by Seibel. He argues that the field has grown to include not only those who can develop operational software but also those who devise the tools, frameworks, and methodologies that support the development, analysis, debugging, and implementation of applications within a constantly evolving landscape.
He emphasizes the importance of deeply understanding what users require and making certain that the program functions harmoniously within larger frameworks. Deutsch underscores the necessity for programmers to have a comprehensive grasp of their function within the larger system and its ongoing evolution, while also being proficient in various competencies that extend beyond mere code creation, including evaluating, architecting, documenting, and maintaining clear dialogue with colleagues, superiors, and customers.
Bernie Cosell's emphasis on code readability and structure: advocating for a design process that prioritizes clarity and maintainability
Cosell stresses the significance of crafting code that operates effectively for computers while also being easily understandable and transparent for other programmers, a point that has been underscored by Seibel. He acknowledges the allure of boosting efficiency and accelerating program execution; however, he prioritizes the simplicity and straightforwardness in comprehending and updating the code, particularly for software designed to last and be altered by numerous developers over time.
Cosell is convinced that a profound grasp of most programming aspects enables their creation in an uncomplicated and direct manner, bypassing the need for intricate tactics or sophisticated functionalities. He advocates for continuous improvement of designs, leading to code that is both comprehensible and straightforward to upkeep. Additionally, he encourages programmers to continuously improve their coding skills, allowing for the steady improvement of the software alongside their evolving understanding of the problem and the refinement of their approach to solving it, leading to code that increasingly becomes more refined and robust over time.
Peter Norvig observed that modern programmers are increasingly inclined to utilize pre-built components instead of developing every part from the ground up.
Seibel observes that Norvig's broad experience at Google, coupled with his varied career history, has endowed him with a distinctive viewpoint regarding the characteristics that distinguish a skilled programmer. In today's programming environment, he acknowledges that developers are frequently tasked with combining pre-existing elements and libraries rather than creating every aspect from scratch.
This transition, he argues, necessitates that software developers adapt their methods, show a keen interest in understanding and working with the fundamental elements of existing software, and possess the ability to integrate systems developed by different people in various settings. Norvig underscores the importance of a proactive mindset in which programmers demonstrate readiness to acquire new skills.
Other Perspectives
- While Jamie Zawinski cautions against overly ambitious sequels, one could argue that significant innovation often requires bold steps and that playing it too safe can lead to stagnation and missed opportunities for breakthroughs.
- Douglas Crockford's push for simplicity might overlook the fact that certain complexities are inherent and necessary in software systems to provide advanced functionalities and cater to diverse user needs.
- Peter Deutsch's preference for Python's modularity and explicit dependencies might not acknowledge the benefits that other languages like Smalltalk offer, such as rapid prototyping and a highly interactive development environment, which can be crucial in certain development contexts.
- L. Peter Deutsch's emphasis on the broad spectrum of skills needed in software development could be seen as undervaluing the specialized expertise that focused "coders" bring to the table, which can be critical in highly technical or niche areas of software development.
- Bernie Cosell's stress on code readability and structure, while important, might not fully consider scenarios where performance optimization is critical and necessitates more complex, less readable code.
- Peter Norvig's observation about the trend towards using pre-built components could be critiqued for potentially underestimating the value of understanding and crafting the lower-level aspects of software, which can lead to more optimized and tailored solutions.
Additional Materials
Want to learn the rest of Coders at Work in 21 minutes?
Unlock the full book summary of Coders at Work by signing up for Shortform .
Shortform summaries help you learn 10x faster by:
- Being 100% comprehensive: you learn the most important points in the book
- Cutting out the fluff: you don't spend your time wondering what the author's point is.
- Interactive exercises: apply the book's ideas to your own life with our educators' guidance.
Here's a preview of the rest of Shortform's Coders at Work PDF summary: