Doing It Wrong

learn in public

Site Navigation

  • Home
  • Books
  • Work & Play

Site Search

You are here: Home / 2023 / Archives for November 2023

Archives for November 2023

Measuring Temporal and Spatial Complexity

posted on November 27, 2023

What is Big O?

  • way to categorize your algorithm’s time or memory requirements
  • not an exact measurement
  • meant to generalize the growth of your algorithm

Why do we use it?

It helps us make decisions about what data structures and algorithms to use. Knowing how they will perform can help you make the right decision between performance and complexity.

Filed Under: Development Tagged With: Complexity, Reference, Spatial, Temporal

The Pragmatic Programmer: Chapter 2

posted on November 13, 2023

The Universality of a Pragmatic Approach

The Essence of Good Design

Simply put, good design is Easier To Change (ETC) than bad design. Principles of programming well boil down to ETC. It’s a value, not a rule. Values are things that help you make decisions, subtly nudging you in the right direction.

  • [ ] There’s a tension between values and rules in this section. Do you apply either to software development? Do you notice significant differences between values and rules?

Applying a value well enough to guide you will initially require asking how what you’re doing would be influenced by this value until you can internalize that process. It also presupposes that you can know from your current perspective what change would result in ETC. If you can’t figure that out yet, fall back on the ultimate ETC path: try to make what you write replaceable (which will certainly help keep the code decoupled and cohesive). You’ll develop instincts as you go.

When you have a quandary of what’s ETC, you can test yourself by tagging the source code and jotting down your thoughts in your engineering daybook. When the code has to change, reference the tag in your notes and see how it all played out.

Section Challenges

  • [ ] Think about a design principle you use regularly. Is it intended to make things easy-to-change?
  • [ ] Also think about languages and programming paradigms (OO, FP, Reactive, and so on). Do any have either big positives or big negatives when it comes to helping you write ETC code? Do any have both? When coding, what can you do to eliminate the negatives and accentuate the positives?
  • [ ] Many editors have support (either built-in or via extensions) to run commands when you save a file. Get your editor to popup an ETC? message every time you save and use it as a cue to think about the code you just wrote. Is it easy to change?

DRY—The Evils of Duplication

One application of ETC is DRY—Don’t Repeat Yourself. DRY states that every piece of knowledge must have single, unambiguous, authoritative representation within a system. The authors feel this to be one of the most important tools in the Pragmatic Programmer’s tool box.

DRY isn’t just about code—it’s about duplication of knowledge and intent. The acid test for DRY comes when you have to make a change: do you find yourself making that change in multiple places and in multiple different formats? If so, your code isn’t DRY. I think I got burned early on by an over-zealous DRY fanatic—because while I recognize the evils of duplication, I also see bad code written because someone jumps on an abstraction too soon. I like DRY, but Avoid Hasty Abstractions (AHA), because they are quite often the wrong abstraction.

  • [ ] I really appreciate the author’s point that not all code duplication is knowledge duplication. Ask yourself, do they have different reasons to change?

DRY violations can happen in:

  • code
  • documentation
  • data
  • representation
  • developers

You may choose to violate DRY for certain reasons: often times, performance trumps ETC in localized areas. Just contain it (the authors call this localizing the impact), and don’t pre-optimize for performance.

Whenever a module exposes a data structure, you’re coupling all the code that uses that structure to the implementation of that module. Where possible, always use accessor functions to read and write the attributes of objects. I will make it easier to add functionality in the future.

  • [ ] What do you think of the “use accessor functions” advice? What advantages does it offer? What scope should this advice take (…module exposes…)? What might happen if you apply it to all data structures used within the scope of a module as well as those exposed by the module?

Representational Duplication

This is inevitable, but the code that consumes an API and the API itself (or SDK, or external data structure, or RPC call..) will have some duplication—any time you have your code interact with an external entity. Here are some mitigation strategies:

  • internal APIs: look for tools that let you specify the API in some kind of neutral format
  • external APIs: look for a spec (something like OpenAPI) that can be integrated into your code
  • data sources: look for a way to introspect the data’s schema

Interdeveloper Duplication

Basically, siloed teams can create the same tool. Frequent inter-team developer communication can help mitigate this. Whatever you do, make it easy to reuse. If it isn’t easy, people won’t do it.

  • [ ] What can we do in our teams to reduce inter-developer duplication?

Orthogonality

Borrowed from geometry, two lines are orthogonal if they meet at right angles; they are independent. In computing, we usually just mean independent or decoupled. In a non-orthogonal system, there are no quick, local fixes—things are tied together. You get two major benefits from writing orthogonal systems:

  • gain productivity
  • localized changes means that development time is reduced
  • components can be reused
  • when completely orthogonal, component “A” does m things and component “B” does n. Combining them gives you m times n. With overlap, that is less efficient.
  • reduce risk
  • diseased code is isolated
  • system is less fragile
  • probably better tested
  • you’re not tightly tied to a particular vendor, product, or platform We often capture the idea of designing orthogonality through the words: modular, component-based, layered. There’s an easy way to test for orthogonal design: if I dramatically change the requirements behind a particular function, how many modules are affected? In an orthogonal system, the answer should be “one”.
  • [ ] What do you think about the acid test of changed requirements to prove orthogonality?

You should also consider decoupling from “the real world”. Are you using a telephone number as a customer identifier? What happens when the phone company reassigns area codes? Same with postal codes, SSNs, numbers, government IDs, email addresses, etc.

Don’t rely on the properties of things you can’t control.

Toolkits and Libraries

Evaluate external dependencies carefully. Do they impose changes on your code that shouldn’t be there?

Code

You really have to work hard not to couple your code when writing it. To help:

  • keep your code decoupled
  • shy code doesn’t unnecessarily reveal anything about itself and doesn’t rely on other modules
  • avoid global data
  • shared data is shared state
  • avoid similar functions
  • duplicate code is a symptom of a structural problem; maybe use a design pattern

Testing

An orthogonal system is easier to test, and writing unit tests is a good test of orthogonality: what does it take to get a unit test to run—do you import a lot of the rest of your system’s code? Bug fixing is also a good assessment of orthogonality. How localized is the fix? Did you change just one module, or many?

Documentation

The axes of concern are content and presentation for documentation. Markdown does orthogonality well. 🙂

Section Challenges

  • [ ] Consider the difference between tools which have a graphical user interface and small but combinable command-line utilities used at shell prompts. Which set is more orthogonal, and why? Which is easier to use for exactly the purpose for which it was intended? Which set is easier to combine with other tools to meet new challenges? Which set is easier to learn?
  • [ ] C++ supports multiple inheritance, and Java allows a class to implement multiple interfaces. Ruby has mixins. What impact does using these facilities have on orthogonality? Is there a difference in impact between using multiple inheritance and multiple interfaces? Is there a difference between using delegation and using inheritance?

Reversibility

Nothing is more dangerous than an idea if it’s the only one you have.—Emil-Auguste Chartier (Alain), Propos sur la religion, 1938

Requirements will change. There are no final decisions in programming. You need to keep your code flexible, yes, but you also need to think about maintaining flexibility in your architecture, deployment, and vendor integration. Make it ETC.

Section Challenges

  • [ ] Time for a little quantum mechanics with Schrödinger’s cat. Suppose you have a cat in a closed box, along with a radioactive particle. The particle has exactly a 50% chance of fissioning into two. If it does, the cat will be killed. If it doesn’t, the cat will be okay. So, is the cat dead or alive? According to Schrödinger, the correct answer is both (at least while the box remains closed). Every time a subnuclear reaction takes place that has two possible outcomes, the universe is cloned. In one, the event occurred, in the other it didn’t. The cat’s alive in one universe, dead in another. Only when you open the box do you know which universe you are in. No wonder coding for the future is difficult. But think of code evolution along the same lines as a box full of Schrödinger’s cats: every decision results in a different version of the future. How many possible futures can your code support? Which ones are more likely? How hard will it be to support them when the time comes? Dare you open the box?

Tracer Bullets

Use tracer bullets to help you find the target. Tracer bullets allow you to illuminate a small path through all architectural layers of your project. Use this approach to speak to your biggest doubts / risks about what the full project will be. You use this when you’re not 100% sure where you’re going, so you will probably need to adjust your point of aim. But a small body of code has less inertia, so you can adjust quickly.

  • tracer code is not disposable: write it for keeps
  • tracer development is incremental, like the full project it will become
  • the traditional alternative is code in modules (in isolation), then combine into subassemblies, then further combine until you have a complete application
  • tracer code approach has advantages:
  • users get to see something working early
  • developers build a structure to work in
  • you have an integration platform
  • you have something to demonstrate
  • you have a better feel for progress
  • [ ] Have you used tracer code or prototyping in any of your projects? How did it go? Tracer code is distinct from prototyping: prototypes explore a specific aspect of the final system—one layer of the architectural diagram. Prototypes should be thrown away and recoded properly from the lessons learned. Tracer code is lean but complete; prototyping is like reconnaissance for tracer code.

Prototypes and Post-it Notes

Prototypes don’t have to be code-based. They are great for answering just a few questions. You can prototype anything unproven, experimental, or doubtful. Prototypes give you a learning experience, which is their value—not the code produced, but the lessons learned.

How to Use Prototypes

You can ignore:

  • correctness: use dummy data if you want
  • completeness: it has narrow concerns
  • robustness: it’s okay if it crashes and burns every now again again because you haven’t added error checking
  • style: prototype code shouldn’t really have documentation, although you can document the lessons learned

How Not to Use Prototypes

If you’re in an environment where the incompleteness of the prototype is going to be a hangup for your audience (they don’t see the value of the lessons learned), you may be better off using tracer code.

Section Challenges

  • [ ] Marketing would like to sit down and brainstorm a few web page designs with you. They are thinking of clickable image maps to take you to other pages, and so on. But they can’t decide on a model for the image—maybe it’s a car, or a phone, or a house. You have a list of target pages and content; they’d like to see a few prototypes. Oh, by the way, you have 15 minutes. What tools might you use?

Domain languages

Try to write code using the vocabulary of the application domain. In other words, program close to the program domain.

Internal domain languages are written in their host language, ultimately being compiled and run as source code. External domain languages are converted into something the host language can use—there’s a parser. Internal domain languages are ultimately bound by the syntax and restrictions of the host language, while external domain languages face no such restriction.

Section Challenges

  • [ ] Could some of the requirements of your current project be expressed in a domain-specific language? Would it be possible to write a compiler or translator that could generate most of the code required?
  • [ ] If you decide to adopt mini-languages as a way of programming closer to the problem domain, you’re accepting that some effort will be required to implement them. Can you see ways in which the framework you develop for one project can be reused in others?

Estimating

Estimate to avoid surprises. Ask yourself, “Does the person asking for the estimate need high accuracy or a ballpark figure?” The unit of measurement (second, minute, hour, day, month, year, etc.) you use in your estimate is a signifier of its accuracy.

Where Do Estimates Come From?

  • understand what is being asked: have a grasp on the scope of the domain of knowledge
  • build a model of the system: a rough-and-ready mental model will help you examine the original question. E.g., “You asked for an estimate to do ‘X’. However, it looks like ‘Y’, a variant of ‘X’, could be done in about half the time, and you only lose one feature.”
  • break the model into components: identify each parameter; identify how the components interact
  • give each parameter a value: expect errors; find the parameters with the most impact and focus on getting them right
  • calculate the answers
  • keep track of your estimating prowess: keep a record to see how close you got—it’s how you can get better

Estimating Project Schedules

People give multiple estimates in the real world (unless someone pressures them into giving a single answer). Program Evaluation Review Technique (PERT) offers the optimistic, most likely, and pessimistic estimate. The authors are not big fans of this technique.

Often the best way to determine a timetable for a project is by gaining experience on the project (estimates work best when we know what we’re doing). This doesn’t have to be a paradox if you practice incremental development: iterate the schedule with the code.

When asked for an estimate, say “I’ll get back to you.” You get better results when you slow the process down and spend some time going through the steps of creating an estimate.

Section Challenges

  • [ ] Start keeping a log of your estimates. For each, track how accurate you turned out to be. If your error was greater than 50%, try to find out where your estimate went wrong.

Filed Under: Development Tagged With: Book, Notes

The Pragmatic Programmer: Chapter 1

posted on November 1, 2023

Distinguishing Characteristics of a Pragmatic Programmer

  • think beyond the immediate problem into the larger context
  • take responsibility for what you do
  • instigate change where needed, but don’t let gradual complacency to change claim you
  • understand the context in which you work
  • make your software good enough; refuse bike-shedding or perfectionism
  • develop your knowledge
  • communicate well

It’s Your Life

If you’re unhappy about something that you’ve tried to change, you can use the agency you have in your life to attempt to change it.

You can change your organization or you can change your organization.—Martin Fowler

If your skillset is outdated (or you’re looking to make a change), study interesting stuff in your own time. Your investing in yourself.

  • [ ] How does this resonate with you—do you feel you have agency in your profession?

The Cat Ate My Source Code

A core tenet of the pragmatic philosophy is taking responsibility for yourself and your actions in terms of your career advancement, your learning and education, your project, and your day-to-day work. Admit ignorance and mistakes directly and with honesty. This helps to build trust, which is key to making an environment you want to work in.

Take Responsibility

Responsibility is something to which you actively agree—it’s your commitment that ensures your responsibility is done right. When something goes wrong (that happens!), don’t blame someone or something else; admit it and offer options, not lame excuses. To that end, you can rubber-duck the scenario to see how it will sound to your boss / client. Roleplaying the situation will help you find the low-hanging fruit of “Have you tried this…” and “Didn’t you consider that?”

  • [ ] How does this sit with you—does it tend towards hard-nosed or professionalism?

Section Challenges

  • [ ] How do you react when someone—such as a bank teller, an auto mechanic, or a clerk—comes to you with a lame excuse? What do you think of them and their company as a result?
  • [ ] When you find yourself saying, “I don’t know,” be sure to follow it up with “—but I’ll find out!” It’s a great way to admit what you don’t know, but then take responsibility like a pro.

Software Entropy

Entropy in software is called “software rot”. Some people call it “technical debt”. The most important contributing factor is the psychology / culture of work on the project. Why do some projects successfully fight that tendency toward disorder? Hopelessness can be contagious. If you don’t think it can be fixed, you won’t try. The key to building a culture of hope is “Don’t live with broken windows.” Broken windows can be:

  • bad designs
  • wrong decisions
  • poor-quality code

You can fight software entropy / rot / tech debt by choosing a problem to fix each time you work on the project. See Refactoring by Martin Fowler.

  • [ ] Have you ever worked on project where entropy was winning? I.e, so much of the project was broken that you didn’t put forth your best effort in your work, because you knew that your best effort would be a waste given the current state of the software.

Section Challenges

  • [ ] Help strengthen your team by surveying your project neighborhood. Choose two or three broken windows and discuss wit your colleagues what the problems are and what could be done to fix them.
  • [ ] can you tell when a window first gets broken? What is your reaction? If it was the result of someone else’s decision or a management edict, what can you do about it?

Stone Soup and Boiled Frogs

Every now and then, you might need to emulate the soldiers from the Stone Soup folktale. You can summarize all of this as “Be a catalyst for change.” Here are the takeaways:

  • You know what needs to be done, but if you ask for permission to do the whole thing, you’ll get denied / have to fight too hard for it (“start-up fatigue”).
  • work out what you can reasonably ask for
  • develop the MVP well
  • show it to people
  • say “of course, it would be better if we added…”, pretending it’s not that important
  • People find it easier to join an ongoing success, so show them a glimpse of the future that you want them to rally around.

The other side of the folktale belongs to the villagers: they were a textbook example of gentle and gradual deception. Projects can slowly and inexorably get totally out of hand. Remember the big picture. Another witticism captures this well: boiling a frog. Don’t let the water temperature gradually increase until your project dies.

Section Challenges

  • [ ] While reviewing a draft of the first edition, John Lakos raised the following issue: The soldiers progressively deceive the villagers, but the change they catalyze does them all good. However, by progressively deceiving the frog, you’re doing it harm. Can you determine whether you’re making stone soup or frog soup when you try to catalyze change? Is the decision subjective or objective?
  • [ ] Quick, without looking, how many lights are in the ceiling above you? How many exits in the room? How many people? Is there anything out of context, anything that looks like it doesn’t belong? This is an exercise in situational awareness, a technique practiced by folks ranging from Boy and Girl Scouts to Navy SEALs. Get in the habit of really looking and noticing your surroundings. Then do the same for your project.

Good-Enough Software

Whatever this means, it doesn’t mean poor-quality software. It does mean that all software involves trade-offs. You should involve your users in that trade-off: how good do they want their software? The bottom line: make quality a requirements issue. Know when to stop, like an artist painting on a canvas. It will never be perfect, so you need to develop a sense of when software is good enough.

  • [ ] How does this sit with you? Do you accept the idea that your software cannot be perfect, or do you think that’s a self-fulfilling mentality?
  • [ ] Why would you want to involve your users in the trade-offs of “good-enough” software.

Section Challenges

  • [ ] Look at the software tools and operating systems that you use regularly. Can you find any evidence that these organizations and/or developers are comfortable shipping software they know is not perfect? As a user, would you rather (1) wait for them to get all the bugs out, (2) have complex software and accept some bugs, or (3) opt for simpler software with fewer defects?
  • [ ]Consider the effect of modularization on the delivery of software. Will it take more or less time to get a tightly coupled monolithic block of software to the required quality compared with a system designed as very loosely coupled modules or microservices? What are the advantages or disadvantages of each approach?
  • [ ]Can you think of popular software that suffers from feature bloat? That is, software containing far more features than you would ever use, each feature introducing more opportunity for bugs and security vulnerabilities, and making the features you do use harder to find and manage. Are you in danger of falling into this trap yourself?

Your Knowledge Portfolio

Your knowledge and experience are important day-to-day assets. In tech these assets expire, because your knowledge becomes out of date as new techniques, languages, and environments are developed. Your ability to learn new things is your most important strategic asset.

The facts about computing, knowledge about application domains, and experience developing is called your knowledge portfolio. There are similarities to a financial portfolio:

  1. serious investors invest regularly—as a habit
  2. diversification is the key to long-term success
  3. smart investors balance their portfolio between conservative and high-risk / high-reward investments
  4. investors try to buy low and sell high for maximum return
  5. portfolios should be reviewed and rebalanced periodically
  • [ ] Do you find this an apt comparison?

Building Your Portfolio

The process of learning and staying current will expand your thinking, open new possibilities and ways of thinking. Philosophically, you’re aiming for:

  • invest regularly: consistency is key
  • diversify: technical and non-technical skills, not just your day-to-day tech—be T-shaped
  • manage risk: go out on a limb—it may pay off, but have the stability that means that when a technology falls out of favor it doesn’t end you
  • buy low, sell high: it’s about adding value to you
  • review and rebalance: keep a sense of the field—what should you learn next?

Some goals you could set (not exhaustive; not prescriptive):

  • learn at least one new language a year: it broadens your thinking
  • read a technical book each month
  • read non-technical books, too
  • take classes
  • participate in local user groups and meetups
  • experiment with different environments
  • stay current

Each time someone asks you a question that you don’t know the answer to you have a tremendous opportunity to learn. You should admit that you don’t know, but use that as a springboard to find the answer. Find someone who knows, because talking to other people expands your personal network. The Army tool of “hip-pocket training” is a good philosophy to make sure that you aren’t wasting time that could be used for investing in your knowledge portfolio.

It’s not enough to consume—you must think critically about what you’re consuming. You can practice this discipline by asking:

  • the “five whys”
  • who this benefits
  • what the context is
  • when or where this would work
  • what makes this a problem
  • [ ] Does this feel empowering or exhausting?

Section Challenges

  • [ ] Start learning a new language this week. Always programmed in the same old language? Try Clojure, Elixir, Elm, F#, Go, Haskell, Python, R, ReasonML, Ruby, Rust, Scala, Swift, TypeScript, or anything else that appeals and/or looks as if you might like it.
  • [ ] Start reading a new book (but finish this one first!). If you are doing very detailed implementation and coding, read a book on design and architecture. If you are doing high-level design, read a book on coding techniques.
  • [ ] Get out and talk technology with people who aren’t involved in your current project, or who don’t work for the same company. Network in your company cafeteria, or maybe seek out fellow enthusiasts at a local meetup.

Communicate!

A good idea is orphaned without effective communication.

  • know your audience: understand the needs, interests, and capabilities of your audience; from Neuro-Linguistic Programming (NLP), “The meaning of your communication is the response you get.”
  • know what you want to say: plan what you want to say by writing an outline that communicates what you want to say
  • choose your moment: make what you’re saying relevant in time as well as context (“Is this a good time to talk about…?”)
  • choose a style: attempt to tailor your message to your audience (are they already SMEs?); when in doubt, ask
  • make it look good: aesthetics are a signifier of trust
  • involve your audience: the documents we produce are often less important than the process we go through while producing them—get feedback, bring people together, pick brains
  • be a listener: ask good questions or ask people to restate the discussion in their own words; you might learn some amazing stuff this way
  • get back to people: always respond, even if with something like “I’ll get back to you later.”
  • [ ] Do you have any experience with NLP? What do you think of “The meaning of your communication is the response you get.”?
  • [ ] What challenges do you face in communicating (written and orally) within your profession? What insights do you have to effective communication that would help the group?

Documentation

Build documentation in; don’t bolt it on. Keep code and documentation together (they have the same reason to change). Commenting source code well is a part of good documentation. Don’t comment so much that it loses value. For non-API comments (not public facing), discuss why something is doneits purpose and goal. The code itself should already show how it’s done.

I also find tests to be a good form of documentation.

  • [ ] How and when do you comment your code? What are some good code comments, tests, or commit messages that have really helped you out when writing software?

Section Challenges

  • [ ] There are several good books that contain sections on communications within teams, including The Mythical Man-Month: Essays on Software Engineering [Bro96] and Peopleware: Productive Projects and Teams [DL13]. Make it a point to try to read these over the next 18 months. In addition, Dinosaur Brains: Dealing with All Those Impossible People at Work [BR89] discusses the emotional baggage we all bring to the work environment.
  • [ ] The next time you have to give a presentation, or write a memo advocating some position, try working through the advice in this section before you start. Explicitly identify the audience and what you need to communicate. If appropriate, talk to your audience afterward and see how accurate your assessment of their needs was.

Filed Under: Development Tagged With: Book, Notes

  • « Previous Page
  • 1
  • 2
  • 3

Profile Links

  • GitHub
  • Buy Me a Coffee?

Recent Posts

  • Event Listeners
  • A Philosophy of Software Design
  • The Programmer’s Brain
  • Thoughts on Microservices
  • API Design Patterns

Recent Comments

No comments to show.

Archives

  • May 2025
  • September 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • December 2022
  • December 2021

Categories

  • Development