2022 Reading List

I’m frequently asked what resources I’ve found most useful on my learning journey. Here are my recommendations for both technical and non-technical people who want to improve organizational outcomes. These are in no particular order.

Engineering the Digital Transformation: (Everyone) The hardest part of organizational improvement is getting alignment on goals. Gary Gruver’s book and related training are excellent for this. Gary has a no-nonsense style of communicating how to improve the flow delivery and relentlessly improve quality processes in multiple contexts. He manages to do this in ways that everyone, no matter how technical, can understand. This is critical for establishing a baseline understanding so everyone can work towards common goals.

Modern Software Engineering: (Developers) From Dave Farley, one of the authors of Continuous Delivery, comes a concise guide to what it means to be a modern software engineer and how to approach the work with a real scientific and professional mindset. This book is the book I’ve needed for years. Not only does it have some really useful information on how to design systems that are optimized for testability and feedback, but it also focuses on the mindset required to really deliver valuable solutions. It should be required reading for everyone aspiring to call themselves a Software Engineer.

Continuous Delivery on YouTube: (Developers) Just put this on auto-play while you’re working. So much good information for free covering every topic related to development.

Sooner, Safer, Happier: (Leadership) Jonathan Smart points out the patterns and anti-patterns for organizational improvement. With decades of personal experience with success and failure, Jon knows what he’s talking about. This should be required reading for anyone wanting to improve their organization.

Making Work Visible: (Everyone) Dominica DeGrandis explains how to optimize workflow for both yourself and your team. She explains the power of limiting work in progress to drive things to completion. Do you feel overwhelmed, overloaded with work, and yet nothing is really getting done? Read Dominica’s book. More “no” and less WIP!

Team Topologies: (Everyone) In 1967 Melvin Conway stated “Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.” In Team Topologies, Matt Skelton and Manuel Pais show useful patterns for organizing teams efficiently to improve the flow of information and delivery. I was reading their blog before the book came out and was very excited to meet them at the book signing to thank them for all of their work. This is a must-read before planning your next reorg.

Implementing Domain-Driven Design: (Everyone) Don’t build a single microservice or plan any large organization or architectural refactoring without reading Vern Vaugn’s book. This not only covers tactical, code-level DDD but also discusses the broader strategic organizational implications and design patterns.

Software Engineering at Google: (Everyone) Tones of lessons learned by Google on what works and doesn’t work when delivering value. This is a big, dense book and the valuable insights start immediately.

What books have you found most useful on your journey?


Written on December 30, 2021 by Bryan Finster.

Originally published on Medium

What is a Production Issue?

Continuous Delivery requires an entirely different mindset from classical artisanal delivery. For example, we always prioritize operations over development. If we cannot deploy something to our production environment, then nothing else matters. Therefore, the first feature to deliver is the ability to deploy to production. We deploy a Minimum Viable Deployable “hello world” UI or service to validate we can. Then we keep adding small changes very frequently to keep validating we can and to get operational feedback on those changes.

Another mindset shift is the definition of “Production”. Our primary product on a product team should always be the pipeline. It needs to always be green and it needs to have all of the quality and compliance gates coded into it to ensure our confidence to release. Only the pipeline defines releasable. If we are not releasable, our highest priority is to become so. The implications of this are incredibly important for teams who supply products and services to product teams to understand.

All of the following should be P1 issues from the perspective of platform teams:

  • The product teams cannot deploy to production due to a production outage.
  • The product teams cannot deploy to production due to instability in the testing/staging environments we manage.
  • The product teams cannot deploy to production because our security tool is generating false positives.
  • The product teams cannot deploy to production because our CI tool is unstable.
  • I cannot deploy to production because my laptop is broken.

All of these are “production impacting issues” because if any of them are true then we cannot safely FIX production when something breaks. We do not downgrade an issue because “that’s only an issue in Staging.” We only downgrade issues that don’t prevent production delivery.


Written on November 1, 2021 by Bryan Finster.

Originally published on Medium

Upgrading the Agile Manifesto

When starting on the journey to agility, the most common path is for an organization to hire a Certified Agile Consultant™ and learn about the Manifesto for Agile Software Development followed by Scrum. Then they spend time catching up to the present 20+ years of learning and hopefully learn that delivery and not the process is the point. Why must everyone start at the beginning? The Manifesto was written over 20 years ago. Some principles have not aged well and most Scrum training reinforces the principles that need upgrading. For example, “we deliver every 2–4 weeks” continues to be a constraint on a majority of teams.

Today, we know that the continuous delivery of value yields better results. We have the ability to get feedback on the quality of our ideas much sooner with less risk and less drama when we learn to solve the problems of “why can’t we deliver value today?”

I Propose a Pull Request…

Code review comments for this post

The Preamble

We are uncovering better ways of developing software by doing it and helping others do it.

Since this was written, we have uncovered practices that are better in every context. Continuous Delivery is one of those practices. We should focus on getting better at that.

We are uncovering better ways of continuously delivering value to the end-user and helping others do it.

1st Principle

Our highest priority is to satisfy the customer through early and continuous delivery of valuable software.

If we just reflect on the implications of this, we mostly only need this principle.

2nd Principle

Welcome changing requirements, even late in development. Agile processes harness change for the customer’s competitive advantage.

Let’s be more explicit.

Incorrect, misunderstood, or changing requirements are the expectation. We embrace this to design a system of delivery to improve the customer’s competitive advantage.

3rd Principle

Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.

This one has aged very poorly. In modern development, we now have the ability to actually achieve the 1st principle by…

Continuously deliver working software, from a couple of hours to a couple of days, with a preference to the shorter timescale.

4th Principle

Business people and developers must work together daily throughout the project.

Projects are not something we should do.

Business people and developers are one product team with birth to death ownership of the outcomes.

5th Principle

Build projects around motivated individuals. Give them the environment and support they need, and trust them to get the job done.

I’m never motivated by a project. I like to see and improve my outcomes. I may be motivated to begin working on a product, but if I find myself in an environment of blame, death marches, lack of user feedback, etc. I will become demotivated. Now, if you ask me to help solve real problems and provide continuous delivery of valuable solutions…

Build product teams with understanding and ownership of the mission, ownership of the solutions, and responsibility for the outcomes in an environment that attracts, grows and retains motivated people.

7th Principle

Working software is the primary measure of progress.

Well, nearly…

How often do you find that “working software” is interpreted as “meets the required spec?” Our goal is valuable solutions to business problems.

Valuable outcomes are the measure of progress.

11th Principle

The best architectures, requirements, and designs emerge from self-organizing teams.

This one is often misunderstood. It needs clarification.

The best architectures, requirements, and designs emerge from product teams who have ownership of the problem to be solved, ownership of how to solve it, and responsibility for the outcomes.

12th Principle

At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.

I frequently see retros every six months. How can we be more explicit? Why shouldn’t improvement be part of daily work?

The team continuously reflects on things that casue pain, reduce morale, and increase delivery drag on the team, then tunes and adjusts its behavior accordingly using data to validate the changes.

Duena’s 14th Principle

My friend Duena Blomstrom’s response to this post proposes another principle. What we do is mostly about people, not technology. To be effective, we need as many ideas as possible from everyone on the team. Let’s make that policy explicit.

To be sustainable, happy and high-performing, we are to put the human work and psychological safety of the team, first.

Where can I send my PR?


Written on October 17, 2021 by Bryan Finster.

Originally published on Medium

That's Not Continuous Delivery

Continuous delivery is a powerful tool for delivering changes safely, reliably, and more frequently with less toil and burnout. CD is also the best tool for uncovering why we can’t do these. However, this is only true if we are really using CD.

CD Improves Everything

In 2017, the authors of Accelerate stated, “Continuous delivery improves both delivery performance and quality, and also helps improve culture and reduce burnout and deployment pain.”

Many of us have found this to be true. However, achieving these outcomes means really understanding what a CD workflow is. We get some tips from the authors of the book Continuous Delivery who stated these principles:

  • Build quality in
  • Work in small batches
  • Computers perform repetitive tasks, people solve problems
  • Relentlessly pursue continuous improvement
  • Everyone is responsible

They do an excellent job in their book at explaining the anatomy of a pipeline, how testing should work, and many other important patterns. In 2021 one of the authors, Dave Farley, released a companion book Continuous Delivery Pipelines where he describes patterns for multiple contexts. The problem is that people don’t read books.

Fake CD

Fake CD occurs when an organization declares victory by stating “we are doing continuous delivery!” while implementing one or more anti-patterns. This isn’t to say that teams who are working to solve the problems and haven’t reached the minimum definition are doing “fake CD.” They just haven’t gotten there yet. CD is an engineering discipline, not only a set of principles. It is a set of objective practices that enable us to safely release working solutions on-demand without drama. I’ve frequently seen organizations claim to be “doing CD” because they have an automated deployment process. CD isn’t Jenkins, CircleCI, or any other tool. It’s how we use those tools. Recently I’ve had conversations where they were claiming to use CD and no amount of explaining would change their minds.

In one instance they delivered monthly.

“The definition of CD is we can release on demand and our demand is monthly.”

No, one of the practices of CD is that we are always releasable so that we can release on demand. This isn’t about scheduled delivery. We need to accommodate unscheduled delivery in an emergency and do it safely. When challenged to pick up a copy of Continuous Delivery, they scoffed at the idea.

In another instance, they were using a hotfix process. A hotfix process is where we use an exception process to deliver change in an emergency because our normal process is too slow. This generally means skipping quality gates when there is already a dumpster fire. The practices of CD act as a forcing function to improve those gates.

“We are on the path of improvement and that’s CD!”

No, delivering on-demand without doing it safely is not CD.

We Built Yardstick

Using bad definitions of CD harms outcomes, causes organizations to set incorrect goals, and harms continuous delivery as a brand. “Oh, we tried CD but it’s was just too risky.” They did it wrong. Several of us a the 2021 DevOps Enterprise Summit decided we needed to do something to help people aim for the right target. We spent many hours hashing out answers to a question, “what are the absolute minimum practices that apply to every delivery context that define continuous delivery.” To see the benefits of CD, this minimum set of practices must be adhered to. We published the MVP for CD, MinimumCD.org, at the end of the conference. Within days we had a number of people signing on to our effort including the co-author of Continuous Delivery, Dave Farley.

We agree this is the yardstick. “You must be this tall to claim CD.” This isn’t gating or elitism. We know that doing these things improves organizational outcomes and the lives of teams. We know doing less than this will not deliver those outcomes. This is the minimum threshold. Take a look. We welcome contributions. If you agree, submit a PR and add your signature.

MinimumCD.org


Written on October 17, 2021 by Bryan Finster.

Originally published on Medium

Agile Rehab

I’ve been a developer for a while. I’ve delivered using every industry fad that management has bought into. In 2003, the organization I worked for wanted to bring some discipline to our ad hoc processes so they sent senior developers and team managers to PMI training to become certified project managers. We implemented a disciplined SDLC with process gates, learned to calculate the critical path on a Gantt chart, and spent a lot of time pretending that product development was deterministic over large timescales and feature sets. This failed, just as it almost always does.

Later, new management wanted to adopt agile development practices to improve things. Everyone went to the two-day Agile training, tried to translate these new terms to SDLC processes, and learned to create two-week project plans. Water-scrum-fall. This was more effective for finding out when we were trending late but didn’t really improve delivery. We still delivered changes every quarter, it’s just that all of the terms changed and we were “Agile”. In fact, we had Agile Maturity Scores™ to show exactly how agile we were.

Eventually, we had a real, objective goal. We needed to deliver at least bi-weekly instead of quarterly. We needed to solve all of the technical and process problems required for continuous delivery (CD). Our original goal was bi-weekly, but we decided that daily or more was a better goal. There were many good reasons for this, but the most important was that when there is an impacting incident with a direct downtime cost of thousands of dollars a minute and an indirect cost of far more than that, we need to have the ability and confidence to fix things quickly without making things worse. CD enables this if teams practice delivering very frequently. As we dug into CD, we quickly discovered we knew all the Agile terms and ceremonies, but we had no idea how to be agile. We had to fix that to deliver daily. If you are struggling with “Agile”, I hope our learnings help.

Use the Right Definition for “Agile”

There are two common definitions for “agile”:

**ag-ile (adjective):**Able to change directions quickly and easily. Nimble.

This “agile” is an outcome of having the ability to adjust priorities and improve outcomes based on the feedback from the end-user.

**Ag-ile (proper noun):**A product marketed by Agile Industrial Complex certified consultants to pay for their boats.

This “Agile” is focused on selling cookie-cutter solutions and “Agile Transformation”.

The difference between the noun and the adjective is that the adjective is focused on delivery and feedback. The noun is focused on standardized processes, typically by claiming we all need to do Scrum and get better Agile Maturity™ Scores. Nouns and cookie-cutter solutions are easier to sell and implement than results.

I’ve had so many conversations where someone says, “but the Scrum Guide says…” I had one person tell me that using a “definition of ready” before beginning work wasn’t valid because it wasn’t in the Scrum Guide. OK. So? DoR is a good thing. The Scrum Guide also says both that it’s incomplete and that if you aren’t doing everything in it then you aren’t doing Scrum. Our goal isn’t Scrum and the Scrum Guide isn’t the law of agile software development. It’s the opinion of one group of people and..

Stop Using Maturity Models

Maturity models are bunk. They are a list of things someone else developed for their context. Yes, many of these have good ideas in them. Harvest the good ideas. However, no customer cares about our “maturity score” and we aren’t ever “mature”. Instead, we measure indicators for the continuous delivery of value to the end-user.

Have an Agile Mindset

What is the real problem we are trying to solve? Development is complex. It is not merely an assembly line process where we know the end state before we begin. We are doing product development. Every release is a prototype that we hope meets the needs. The reality of software development is that one or more of the following is true:

  1. The requirements are wrong
  2. We’ve misunderstood the requirements
  3. The need will change before we deliver

We are always talking about the “agile mindset”, but what is that really?

“We are probably wrong. We need to optimize our processes for being minimally wrong so we can quickly adjust to become less wrong.”

What we are doing is establishing a pattern of delivery that mitigates this reality. Real agility is using the scientific method to verify or falsify our value hypothesis. We need to deliver very small batches of work very frequently to get rapid feedback from users so we can adjust. We need to reduce the cost of change and improve our quality processes to support our ability to do that.

For Agility, Think Smaller

Start with a simple question, “why can’t we deliver working changes today?” Continuous delivery is the tool for uncovering and fixing the impediments to agility. There will be technical and process challenges to solve. How do we automate CI and delivery while remaining secure, compliant, stable, available, and delivering useful features? How do we make code changes that are evolutionary so we do not need to branch and wait for features to be complete before delivering? How do we break down work so we can deliver working features in less than 2 days? How do we get better information? How do we work as a team better?

As the problems are tackled, we’ll find that many of the things taught in Agile training are useful. Others don’t add value to us. We want the minimum viable process to enable the continuous delivery of value and we want to engineer solutions to keep the process overhead low.

Don’t Keep Starting Over with the Manifesto

The Manifesto for Agile Software Development begins with “We are uncovering better ways of developing software by doing it and helping others do it.” That was written over two decades ago. There is much good there, but many things have been proven since then and do not need to be uncovered. CD is one of those things. If you are trying to become agile, don’t start with the Manifesto. Don’t start with Scrum. Don’t try to follow the rules, because there are none. Focus on the continuous delivery of value to the end-user and ask “why can’t we deliver today?” Use the principles and practices of continuous delivery to uncover waste and pain. Relentlessly improve to remove them. Your customers will be happier. So will the teams.


Written on September 26, 2021 by Bryan Finster.

Originally published on Medium

Development Pattern TLAs

There are many common three-letter acronyms (TLAs) used in the software industry; TDD (Test Driven Development), BDD (Behavior Driven Development), DDD (Domain Driven Design), etc. However, there are a few lesser-known TLAs we should be aware of.

JDD

Jenga Driven Development is the haphazard approach to building systems where no thought is given to sustainable delivery. Eventually, the system becomes so fragile that it’s too dangerous to change. At that point we knock over the tower, “modernize the tech stack”, and hope we get different outcomes.

Credit to Antonio Cangiano for this gem.

RDD

Resume Driven Development is the approach used when the primary goal is to expand the technologies we can claim to have used rather than delivering value. The indication for this is tech stack and architectural complexity.

“What language are you using?”

“With our event-driven, micro-service architecture we aren’t constrained to a single language like less evolved teams. We use C#, Golang, Node, Python, Clojure, and Haskell. Our UI framework allows us to use Angular, React, and Vue. We like the flexibility.”

“Wow! That’s impressive! What does your product do?”

“It’s a recipe organizer.”

CDD

Conference Driven Development is what happens when we (or executives) get excited about the shiny new things seen at the last conference. The outcome of this is that we stop everything we are doing to re-architect our system, move everything to the cloud, rewrite everything using Qlang, or chase any other process/technology shown on stage to replicate their outcomes without regard to our own context or all of the problems the presenters didn’t talk about in 30 minutes of marketing.

PDD

Process Driven Development is when “how” we work is more important than the outcomes. An example of this is any “Agile Framework™” that includes metrics for how well the framework is being executed.

DDD

Deadline Driven Design. Related to JDD, but implemented much more rapidly. Deadline Driven Design is what occurs when we pull out all the stops to have teams meet a fixed (or expanding) scope and date project and pressure them to ignore things like testing, feedback loops, and basic design practices so we can “make the date”. We declare success on delivery and hope we can get a promotion before anyone notices the outcomes. This always ends in tears and often for many years afterward, but only for the teams. Anyone who can get another job will.

GDD

Grenade Driven Development is the pattern we use when we want to ensure we distribute responsibility as widely as possible by creating functional silos.

Requirements are written and lobbed at development. Coding is completed and lobbed at QA. Testing is completed and lobbed at Ops. Finally, the grenade blows up in the face of the end-user and everyone points at everyone else. Success!

FDD

Factory Driven Development is the process where we send requirements to a feature factory coding mill and expect them to be implemented quickly and without question. Programming is just glorified typing, after all.

“Stay in your lane, Code Monkey.

MDD

Management Driven Design is when we architect our system around the desired management reporting structure. Our system will have no clear domain boundaries and any change will require a lot of multi-team coordination, but at least we made HR’s job easier. Conway be damned.

BDD

Bourbon Driven Development is the process we can use when our quality gates are so robust that we can confidently code while enjoying a nice, aged Kentucky bourbon neat. This should always be the goal.


Written on September 10, 2021 by Bryan Finster.

Originally published on Medium

Choosing the Right Tools

Whenever we want to solve a problem or build something useful, we have decisions to make about the tools we choose. Choosing the right tools requires a good understanding of the problems. Often though, there is a tendency to jump to familiar tools regardless of the problem. “I love the balance of this hammer. It just works well.” The hammer is probably awesome and it may have been used successfully to drive screws for years, but what if we stood back and reflected on why we are choosing a hammer to drive screws?

In 2007, Dave Snowden published “A Leader’s Framework for Decision Making” in the Harvard Business Review. In that article, he introduced the Cynefin framework (**ku-nev-in).**Cynefin is a way to categorize the kind of problem we have so we can apply the right tools. Briefly, there are four kinds of problems.

Cynefin Diagram

You start by approaching each problem, categorizing the problem, and then applying the right tools to solve it. For example, tying your shoes seems like an obvious problem that requires best practices.

However, when we review the multiple contexts for tying shoes, for everything from dress shoes to hiking boots, it becomes clear that it is contextual, complicated. We need to use good practices.

In software delivery, applying the wrong mental model for the problem space is very common and negatively impacts outcomes throughout the value stream. An example of this is building an effective quality process where using the wrong mental model for software quality causes us to verify the wrong way.

Is Development Complicated?

It is common to categorize software development as “complicated” because we write code that implements the requirements. We can then verify quality by testing that the code matches the requirements. Using this mental model, the rational thing to do is to create a QA department to verify that the development teams are implementing the specifications correctly. We want to deliver relatively small batches to the QA team, but we do not want to deliver to the end-user until QA has ensured quality. This is using the tools from assembly line manufacturing where quality can be easily verified.

Let’s build a car. We establish an assembly line and begin construction. As individual parts are built, they are tested for adherence to spec using test fixtures and then assembled into larger units where other test fixtures run additional tests on the integrated components. Finally, the car is driven to make sure it all works together. So far, we have a model that resembles unit, integration, and end-to-end testing patterns. However, we’ve made a huge mistake. No one wants to buy our car.

We’ve built the wrong thing using the best quality practices we know, and yet it is still poor quality because it doesn’t fit the need.

This happens all of the time in software development because we over-simplify the problem at hand. We are not assembling and delivering code. It only appears that way on the surface. In Cynefin, if you over-simplify the problem, you will fall into the chaotic problem space and will need to react to recover. This feels very familiar.

Development is Complex

What is the right mental model? Using our car example, we skipped right past the most critical part, “what should we build? How should we build it?” The analogy of development as an assembly line is almost entirely wrong. If we have automated build and deploy systems, those are our assembly lines. They have automated the complicated good practices and obvious best practices. However, everything that happens before we submit code to our assembly line is complex and emergent. The correct analogy is the design studio.

We are not assembling a car from specs. We are developing a new car, getting feedback as we go, iterating towards the specifications we want to build, and designing the most efficient way to build it. We are making decisions about components. Do we need to design new brakes? Can we use off-the-shelf Brembos? What interface changes do we need to make to use those brakes on our new prototype? Etc.

In manufacturing, it is very expensive to change these decisions before we construct our assembly line. So, design precedes construction, we lock in as many decisions as we can after extensive research, and we hope we’ve developed a car people want.

In software, the economic forces are entirely different. The most expensive part isn’t building and running the assembly line. In fact, creating the build and deploy automation is the cheapest thing we do. The cost comes from the R&D work of deciding how to solve the problem and writing code to see if it solves it. To mitigate that cost, we need to design a series of feedback loops to answer several quality questions: is it stable, performant, secure, and fit for purpose?

We can build a quality process that can verify stability, performance, and security before we deliver anything. However, “fit for purpose” is subjective. For that, we need to build a quality process that continuously verifies that with users. We need to do this by delivering the smallest changes we can verify to reduce the cost of being wrong. We need to understand that we are not assembling identical cars. Every single delivery from our assembly line is a prototype that is different from the one before. We cannot use static test fixtures built by another team. In fact, that’s destructive to our quality process because waiting for someone else to build the fixtures means we will build bigger things to test and drive up the cost of being wrong. Our test fixtures can only verify that we are building what we think is needed. We must constantly adjust them as our understanding changes. We are prototyping fixtures and building things to fit the fixtures so that if it is fit for purpose we can replicate success.

Focusing on continuous integration and delivery can make us more efficient at delivering small batches of work. Designing our quality process to optimize for user feedback from ever smaller batches of work will make us more effective at delivering the right thing. To do this well, we need to stop over-simplifying what we do. Software development is complex and if we apply the wrong tools to this problem space…

we will fall into chaos.


Written on August 20, 2021 by Bryan Finster.

Originally published on Medium

Constructive Pessimism

Hope creep is the plague of value delivery. We assume that since something worked in the past it will work again. We forget how much pain we’ve had in the past. “How hard can it be to build that? Looks pretty simple to me!” This is a big-bang, waterfall delivery mindset. We need a mindset that embraces reality. We need to internalize that everything is probably wrong and broken so we can prioritize our actions correctly.

There are many technical and process practices required for executing continuous delivery. It’s not easy, some are counterintuitive, and it requires a high degree of team and organization discipline. However, the rewards for doing it for the customers, the organization, and the individual developers are so great that baffles me that it’s not the standard industry practice. It does require that we modify our priorities though.

Priority 0: Can we get to production or, minimally, a production-like environment? This is feature 0 and should block all other feature work. Nothing we do matters if we can’t deliver.

Priority 1: KEEP THE PIPELINE GREEN! If anything blocks delivery, stop and fix it.

The Reality of Development

I’ve recently joined an organization where security is taken far more seriously than most organizations. Delivery pipelines are secure as we know how to make them and known attack vectors that can be detected with the pipeline will block builds. My first task is to deliver delivery pipeline observability to help any team using our tools find and resolve constraints in the flow of value delivery. This is one of my passions and there aren’t very many options available in the industry today for solving this problem, so it’s very cool.

We are building the application using React and other standard tools because we want to stay as close as we can to patterns we know will pass the pipeline security gates. As an advocate and mentor on CD practices, the first task we needed to accomplish was to deliver the pipeline. Should be simple

npx create-react-app <dashboard-name>

60 seconds later we have something we can build on!

For any new application, feature 0 is “can we deploy the application?” So, now we just need to deploy this to meet that requirement so we can start on feature 1. How hard could it be?

Going through the internal CD platform documentation to deploy a React application yields some simple configurations that are required. So, armed with confidence, I trigger the pipeline and await success. When I go to the newly deployed site, nothing is there. Blank page. But it worked on my desktop? No errors? WTF? Pulling the logs shows Content Security Policy violations. Going back through the docs shows I missed a step. To avoid CSP issues, React needs the INLINE_RUNTIME_CHUNK environment variable set to false. Doing that and rerunning the pipeline resolves the issue and we are off to the races.

We start working on the first story. We need a header, a collapsable navigation menu and to display something the first route. We mob the requirements and a few hours later we are looking good!

This is going to be easy! We have a few things to clean up. We refactor some tests and refactor some code. We only need to deploy the newest change and send it out for people to give feedback. We believe in rapid feedback, so we aren’t going to wait for “done” to show this off.!

After triggering the deployment, all of the tests pass. there is no indication anywhere that we have a problem. However, what appears in production is…

And the hair rending that is software development begins again. Inspecting the logs shows dozens of “Refused to apply inline style because it violates the following Content Security Policy…” errors

We’ve run afoul of CSP again after adding MaterialUI. We have a backlog of critical features that we need to deliver quickly. However, it doesn’t matter. We cannot deliver. Our highest priority is solving the reason we cannot deliver. We begin mobbing the issue…

It Could Be Worse

Imagine for a second we executed the way a majority of teams do.

“Pipelines are for delivery. We aren’t ready for delivery until we get signoff on v1.0 so that’s not the priority. When we finish all of the work we need to do for the first version, then we’ll focus on how to deliver. I mean, it works on our desktops so it’s probably fine. We hope we can deliver! We hope our technical decision will work in our environment! We hope it’s what the end-user actually needs!”

I’ll never again work on a team that works that way. Some people reading this are still working this way. I have empathy for you, but no sympathy. We don’t need to work this way anymore. We shouldn’t pretend we can deliver on time if we don’t know we can deliver at all. We need confidence. We need to assume things are broken until we can prove they are not. We need to assume that the ideas or requirements we have for features are wrong until we prove they are not. Building features on hopes and dreams of delivery and correctness is just a waste of time.

Ship first, verify, and keep shipping as fast as possible to keep confidence high that you can deliver and are delivering the right thing. Be pessimistic. Don’t give in to hope.


Written on April 30, 2021 by Bryan Finster.

Originally published on Medium

The Metrics are Lying

I was talking to a friend the other day about how her management is tracking teams’ “maturity” using “DORA Metrics”, the four measures the correlate with high-performing organizations mentioned by DevOps Research and Assessment in “Accelerate” and the State of DevOps reports.

  • Pipeline cycle time (Hard lead time in the book): The time between when a change is committed to version control and when the change delivers to production.
  • Delivery frequency: How frequently changes are made to production.
  • Change fail %: The percentage of changes that require remediation.
  • Mean time to repair (MTTR): The average time required to restore service.

These are becoming very popular in the industry and for good reason; they are good indicators for the health of a delivery process. High-performing teams can deliver changes daily with pipeline cycle times of less than an hour. They have an MTTR measured in minutes and less than 15% of their changes require remediation. So, all we need to do is tell all teams that their goal is to achieve these metrics and evaluate the teams’ “maturity” using these metrics?

We’ve just created another scaling framework silver bullet. Silver bullets have one use case.

Werewolf

Since we are not hunting werewolves on the moors, we need to find tools to solve the problems we have. The trouble is that people hear about the “DORA metrics” and how they correlate to high-performing teams and stop there. Correlation is not causation. Excluding acknowledgments and the index, Accelerate is 229 pages. The metrics are in a table on page 19. Imagine what other useful information the other 228 pages might contain.

Why do high-performing teams have these results?

Ownership

They care about what they are doing, they understand the problem they are trying to solve, they make decisions about how to solve it, they have responsibility for the business outcomes of their decisions, and they have pride in the results.

Practices

They execute a continuous integration development flow with tiny, production-ready changes integrating to the trunk very frequently (no, more frequently than that). When things break in production, they harden their delivery process. When the delivery process takes too long, they make it more efficient.

Organization

They exist in an organization that is optimized for the flow of communication, not for internal kingdoms. Budgets are planned for product lifecycles and value delivery, not annually planned buckets with a “use it or lose it” structure that is optimized to make life easy for accountants.

Culture

Their organization focuses on learning from success and failure. Sharing learning from both is seen as a priority. Why did it succeed? Why did it fail? People are encouraged to speak up when they have an idea, when they think an idea needs improvement, or when they know something is broken.

So, do we throw away the DORA metrics? No, they help us keep an eye on the health of the system. However, they are trailing indicators for poor health, not indicators everything is going well. That’s why “Accelerate” mentions several leading indicators as well (I won’t spoil it for you). We can push teams to improve those metrics and can even get short-term wins that will get people promoted. However, if we are not tracking leading indicators for efficiency and team morale, organizing for the flow of value, and fostering the right organizational culture, then those gains will be lost sooner than later.

Improving outcomes is hard. Read the rest of the book… twice.


Written on March 28, 2021 by Bryan Finster.

Originally published on Medium

Scrum or Kanban?

Should we use Scrum? Kanban? Is Kanban for support and Scrum for development? Is Scrum for newbs and Kanban for elites? Are we mature enough for Kanban?

These questions are a symptom of the Agile Industrial Complex selling certifications and people following the rules to get certified. Scrum or Kanban? The reality is that Scrum is not training wheels and Kanban is not for elites. Scrum is not for product development and Kanban is not for support work. Both are standardized approaches that emphasize small batches of work, feedback loops, limiting WIP, and frequent delivery. Both are done wrong by most teams because they focus on the process instead of the outcomes.

Ultimately it’s irrelevant. Teams who are good at delivery never ask about Scrum or Kanban. Instead, they simply deliver small batches of high-quality work very frequently. This isn’t rocket surgery and they didn’t get there by standardizing on 2-week sprints and reporting their velocity. They got there by focusing on the goals.

What are the goals? We need to have a systematic approach to improving the flow of delivery.

  • Automate our delivery process to remove variance.
  • Stabilize our quality signal and drive detection as far left as possible. This not only includes getting rapid test results but also includes getting feedback from the user to discover if we are delivering the right value.
  • Limit work in progress and finish things before beginning new work.
  • Drive down the size of stories to 1–2 days to improve quality and enable us to pivot rapidly to exploit new ideas that prove valuable.
  • Small changes need to be flowing into version control, built, tested, and delivered VERY frequently (hours, not days) and without humans touching anything.
  • Moderate new work intake to keep lead times low so that ideas do not become worthless while they wait on the backlog.
  • Find ways to increase efficiency to improve throughput.
  • Strive for transparency and trust in everything. Without good communication, we all fail.
  • Measure and improve the things that help our customers.

We need to move fast and fail small and we need to do all of this at a sustainable pace that prevents burnout.

When we focus on Scrum or Kanban, we are focusing on the purity of the process. Our customers don’t care about the purity of our process. Our customers want features to be delivered rapidly and they need those features to be stable, available, secure, and useful. We need to focus on the things that make that happen. Nothing else matters.


Written on March 20, 2021 by Bryan Finster.

Originally published on Medium

Measuring Goals

In my last post, we talked about common metric anti-patterns that are focused on the process instead of value. In this installment, we will cover alternatives that can help us remove waste and improve the flow of value.

Defining Terms

Goal: Something we want to achieve that will improve the performance of our organization. In most businesses, performance can be measured by profitability, sales, etc. If we have goals focused on the business, we’ll probably perform better.

Signal: Something that tells us how we are tracking against our goal. A signal is something that may not be able to be measured directly, but only by inference. For example, quality and value are notoriously difficult to measure, so we must find proxies for those that give us insights.

Metric: An objective measure we can use as a proxy for a signal. More on this later.

Goals

So what are our goals? Some good goals for any enterprise are:

  • Value: We want to improve the value of what we do by creating, saving, or protecting money.
  • Sooner: We want to realize that value sooner with higher quality, less waste, and shorter lead times.
  • More innovation: We want to find even better things to deliver and better ways to deliver them to delight our customers.
  • Stability: We want solutions that our customers can depend on and we want engaged teams who own those solutions.

Signals

Looking at our goals, there are some keywords we can pick out for signals:

  • Money: It’s difficult to track metrics that directly improve income, but we can track metrics that reduce costs. Income will depend on how good the product ideas are, but if we control our costs by reducing waste then it costs less to run experiments to find better ideas.
  • Quality: We want higher delivered quality.
  • Lead Time: We need to iterate faster and shorten the time between idea creation and idea validation with the end-user.
  • Stability: Our customers should be able to depend on our services when they need them and only teams that have high morale and ownership can provide us the level of stability we need.

None of these signals can be measured directly. Even lead time has too many dependencies to be an objective metric that will show meaningful trends. However, we can measure things that give us insights into these.

Metrics

Money

Money and Waste are tightly coupled. More waste costs more money. So if we reduce waste we reduce costs. Waste is a function of process overhead, handoffs, delays, rework, etc. So if we want to reduce waste, we need to measure things that discourage the creation of waste.

To reduce waste, we need to reduce the batch size of changes, deliver them more quickly and validate that they meet customer expectations. We can track this using delivery frequency and defect rates. Depending on the context, customer NPS may also be a valuable insight.

Quality

To improve quality, we need to focus on effective quality gates that provide a stable signal as early as possible. Ultimate quality is determined by the end-user, not adherence to the development specifications (recall from my last article that they are probably wrong), so our most important quality metrics are delivery frequency and **defect rates.**These are trailing indicators, but there are upstream metrics we can measure that will help predict those outcomes. How frequently is code integrated into the trunk and tested? How long does it take to complete a development task? How long does it take between when a change is made and when it delivers to production? Code integration frequency, development cycle time, and build cycle time can be used to reduce the batch size further. If we are integrating code more frequently, the change-set size is smaller, easier to review the tests, and faster to get feedback. Smaller development tasks have more clarity as to how they should behave and contain fewer useless features. Driving down the time from commit to deploy improves the efficiency and effectiveness of the quality gates which also drives out additional waste and cost. It’s critical though that we keep the delivery frequency and defect rates in mind as the critical metrics. They safeguard quality.

Stability

Measuring the stability of an application is relatively straightforward, but that stability will be short-lived without a stable, high morale team. The same goes for efficiency. We can drive to faster delivery with fewer defects and achieve those goals in the short term while burning out the team. That results in turnover or disengagement that will then cost us all of the gains we’ve made and possibly put us worse off than we started. We should ask the team how they feel. Do they own their solutions? Are they provided with the opportunities to keep the code healthy and provide feedback about the expected value of a requested feature? Are they merely a feature factory that is accountable only to dates? Are people fleeing the team, or worse, the company? They need to be business experts who are invested in the business problem, own the solutions, and feel like valued contributors instead of replaceable cogs.

Lead Time

To reduce the lead time, we reduce the internal cycle times for development, build, and delivery. Delivery cycle time depends on development cycle time which depends on build cycle time, so the priorities should be obvious. Again, driving down the cycle times acts as a forcing function for waste reduction and quality improvement.

Metrics Case Study: Formula 1

F1 racing reams measure and continuously improve. Races are not won by the car that can go the fastest. Races are won by consistent usage of the accelerator, brakes, steering, and transmission to reduce wasted motion and get the most from the available speed. They are also won by the teamwork required to drive down cycle times that impact the goals.

KISS

We don’t’ need dozens of metrics for high-level insights. Yes, there are other things we can instrument to find out “why”, but for getting insights into the “what” that will move us towards our goals, we need to measure in combinations that inform our signals.

  • Code integration frequency: Tested changes to trunk per day
  • Development cycle time: Duration from “started” to “delivered”
  • **Build cycle time:**Duration from “integrated” to “delivered”
  • Delivery frequency: Production delivery rate
  • Defect rates: Defects created relative to delivery frequency
  • Team engagement: Team morale and ownership survey

Quality: code integration frequency, development cycle time, build cycle time, delivery frequency, defect rates, and team engagement.

Waste: code integration frequency, development cycle time, build cycle time, delivery frequency, defect rates, and team engagement.

Lead time: Delivery cycle time, development cycle time, build cycle time, and team engagement.

Stability: Operational stability metrics and team engagement

Metrics are not goals; they give us insights into the goal signals. If you measure something in isolation you will risk degrading something else. Always use offsetting groups. Ask yourself, “If we measure this what other things that we care about could go wrong?” and then measure those concurrently. Resist the urge to come up with a “score” that represents everything. If you over-simplify metrics, you will fall into chaos. Our goal isn’t to control the process. Our goal is observability into the value stream so we can improve it. Remember that product development is done by people and for people. People are not machines. Don’t measure people. Measure the flow and give people the observability to improve it.


Written on February 10, 2021 by Bryan Finster.

Originally published on Medium

The Thin Red Line

I was speaking at a DevOps meetup in Finland recently and was asked, “what does DevOps mean to you?” I love that they started the conversation that way. DevOps no longer means Development and Infrastructure teams cooperating and it has never truly been a job or a team. We are not solving a technical problem. We are trying to solve a business problem. How do we more effectively deliver value to our customers?

Note: If someone works for our company, they only become a customer when they buy something from our company.

DevOps is the way we work to improve the continuous flow of value to the end-user. Since our goal is to improve value delivery, we must measure how we deliver value, identify the constraints, and continuously improve the constraints. Everything we do and how we do it must be examined for the value it delivers and be removed if it is not delivering value. This is a long-term process that takes strategy, change management, and constant focus. The reason so many improvement efforts fail is that executives are rarely rewarded for long term improvement. Splashy “Transformation!™” efforts make analysts happy, make good presentations at conferences, and result in job offers. Real improvement requires more than the 2–3 year tenure of many IT executives.

In Star Trek gold is the color of command, blue is the color of science and medicine, and red is operations. Red Shirts are engineers, communications, security, and everything else required to keep the ship running. They are also the first to die when things get tense.

Every organization contains people who, when given the chance, will drive improvement. They care about where they work and the people they work with. They want to make their lives, their co-workers’ lives, and the lives of their customers better. In Gene Kim’s “The Unicorn Project”, the Red Shirts are the cast of engineers and engineering managers who want to make things better. They know where things are broken and decide the best thing to do is to fix things instead of seeking greener pastures. They are passionate, at times subversive, change agents who conspire to improve things that result in larger improvements and better lives for everyone. These characters are based on real stories Gene collected from the DevOps community. In the book, there aren’t very many of them. All of them can comfortably sit at the bar after hours and discuss the next things they will try to fix. It’s a fragile rebellion that could fail if only 2–3 key players left. This is the reality of most grass-roots efforts.

I had a conversation with Gene Kim in 2018 discussing the work of pushing from the grassroots. I’ll never forget what he told me, “This work requires a certain level of not caring about the consequences”. That’s true. It’s risky to challenge the status quo; to “swim upstream”. It requires courage even when you believe that a senior executive approves of the work. It requires challenging people who are not used to being challenged; pushing just hard enough to not get “promoted to customer”. Because of this, there are not very many people willing to do it. However, when change is starting, those are the people who will passionately drive the day to day improvements needed to achieve the mission. If an executive talks about making things better, the Red Shirts are the people who execute. In some cases, they are even the people selling the value to the CIO or CTO. They are the people who will infect others with the mission and expand the effort. That infection doesn’t come from meetings, OKRs, and directives filtering down the leadership chain. It comes from the Red Shirts understanding the goals, seeing the problems, and coming up with solutions.

This work is hard, it’s stressful, and the number of people driven to do it is a tiny fraction of any organization. I’ve spoken to many Fortune 100 companies who need real change to happen to stay competitive. You could literally count their Red Shirts on your fingers. If you want lasting change, you’ll need to find a way to expand the Thin Red Line. Make improvement a valued activity in the culture and recognize those who try.


Written on February 8, 2021 by Bryan Finster.

Originally published on Medium

Metric Mythconceptions

As we try to improve the flow of value to the end-user, the first item that usually gains focus is the productivity of development teams and how to measure it. I’d like to propose that productivity is measured by customer value delivery, not team output. However, that reality is often lost as we rush to find easy numbers to get a handle on measuring teams. Misusing metrics undermines the goals of improvement efforts and playing Whack-a-Mole with metrics anti-patterns is tedious. Hopefully, the anti-patterns cheat sheet will help.

Story points

**Myth:**How long it will take to complete a story

**Reality:**Story points are an abstraction of how complicated, how uncertain, and how big something is expressed by an arbitrary number that is only meaningful to the team, kinda. It’s mostly a holdover from Waterfall estimation and there are better ways to get this done.

**If you measure me by it:**If you want more story points, I’ll increase the number of points by creating more stories or increasing the number of points per story. Agile: where the stories are made up and the points don’t matter. Ron Jeffries said, “I like to say that I may have invented story points, and if I did, I’m sorry now.” The original intent of story points was to create an estimation abstraction instead of explaining to stakeholders, “yes, I said 3 days but that’s three days where I only need to develop and everything goes correctly”. Listen to Ron and split stories to a day or less and stop estimating.

Velocity/Throughput

**Myth:**Higher velocity/throughput means we are more productive!

Reality: Velocity or throughput give us an average of how many things of “about this size” we have completed during a time box so we can plan in the future.

If you measure me by it: If you want more completed in the same amount of time, I’ll create more tasks or increase the number of story points per task. “OK, I’ll just standardize the size of those!”, you say. Sorry, you can’t. You can make story points mean days, but that just means you’re faking agile delivery. If you want day estimates, then use days. Just be aware that estimation is wasteful. That time is better spent delivering and establishing a reliable history of delivery that makes us predictable. Also, consider that every change we make is new work that has never been done before. We aren’t building manufactured housing where each is the same. We are architecting something bespoke. We only have general ideas on time.

Code Coverage %

**Myth:**Code coverage means we are testing more, so we should have minimum code coverage standards.

Reality: Code coverage indicates the percentage of code that is being executed by test code. Test code has two important functions.

  1. It executes the code we want to test.
  2. It asserts some expectation about the results of that code.

Code coverage reporters measure the first activity. The second cannot be measured by robots because the efficacy of an assertion is knowledge work.

**If you measure me by it:**I’ll make sure more code is covered. That’s good, right? Maybe, but maybe not. It costs money to write and maintain test code, so we should eliminate tests that are not providing value. One example of meaningless code coverage is testing anemic publicgetter and setter methods that contain no business logic. Why these are bad is too deep a subject for this topic, but the result is we have methods that do nothing but access data. Testing them means we are only testing the underlying language. We cannot fix the language so we should not test it. Another example is just as wasteful but far more dangerous.

/*
The following is a simple example of real outcomes from mandating code coverage
minimums instead of measuring delivered quality with deploy frequency and defect rates.
This test will result in 100% code coverage but tests nothing.
If you find tests like these, DELETE THEM IMMEDIATLY. They provide less value than
not having a test. It's better to know that this wasn't tested.
*/

@Test
public void testMultiply() {
  /*
  First we execute the multuply method from the myMath class. This will run the method and
  report to coverage reporters that the method is "covered"
  */
  Integer result = myMath.multiply(5,10);

  /*
  Next we create the assertion to tell us if the test passed.
  The assertTrue method in jUnit will return success if the input results in a true response
  */
  assertTrue(true); //Assert something meaningless
  /*
  In this example, we directly pass 'true' so the assertion will always report success and the test
  will never fail.
  */
}

This test increases code coverage and hides the fact that the code is not tested. It’s safer to delete this test than to leave it in place.

Committed vs. Completed

Myth: If we measure teams by how much they completed vs. what they committed to, they will work hard to meet the commitments and we’ll deliver more!

Reality: No battle plan ever survives contact with the enemy. The reason we are not using Waterfall anymore is that the industry has come to terms with the fact that life is uncertain and we cannot make hard plans for the next month, much less the next quarter or longer. One or more of the following is always true:

  • The requirements are wrong
  • We will misunderstand them
  • They will change before we deliver

**If you measure me by it:**There are two actions I will take to ensure I meet this goal. First, I will commit to less work. This might be good because we tend to over-commit in the first place. It won’t make me work faster though. The other thing that I will do is to stick to the plan, no matter what. Defects will be deferred. If higher priority work is discovered, I’ll defer that too. Adherence to a plan ignores value delivery, but measuring me this way means you care more about the plan than the value. I care about the same things you care about if you’re paying me.

Lines of Code

Myth: If I am typing more lines of code per day, I’m more productive, so we need to measure lines of code or the number of commits per day for each developer.

Reality: Development is mostly not a physical activity. A top developer does more thinking than typing. The code is only the documentation of the proposed solution in a format that a computer can understand. The work is coming up with the solution and writing it in a way that is clearly understandable by future developers. I’ve delivered small enhancements before that resulted in the deletion of over 5,000 lines of code.

**If you measure me by it:**I’ll certainly make sure more code is added. I’ll have no real incentive to make the code easy to read or avoid re-use. Copying and pasting code is much easier than spending the time to develop a well-designed business solution.

Number of Defects Fixed

Myth: If we measure the number of defects fixed, there will be fewer defects

Reality: Defect reduction comes from working to improve delivered outcomes, not by focusing on the number of defect tickets we’ve closed.

**If you measure me by it:**Worst case is you pay me a bonus for defects fixed. Adding minor defects that I know where to find and fix is pretty simple. The next worst case is I’ll prioritize minor defects over critical feature work. I’ll also be scheduling meetings to argue if a defect is really a feature because moving it from “defect” to “enhancement” counts as defect reduction as well.

What should we measure?

We need to measure things that improve reduce waste and improve the flow of value. In the next installment, we will discuss some better metrics.


Written on February 8, 2021 by Bryan Finster.

Originally published on Medium

Storytime

The following is fantasy. Any resemblance to persons living or dead is coincidental. However, too many people live this.

ScrummerFall

Jackie is a buyer who needs better tools for forecasting demand so she can make better-informed decisions. She has a list of high value features she needs, so she contacts Dana, the product owner for the forecasting application, to request changes.

Dana discusses the features with Jackie and writes user stories for each feature with acceptance criteria for each story and adds them to the backlog. Since Q1 is half over, Dana schedules the new request to be discussed in the planning meeting for next quarter’s goals. There are lower value features prioritized for Q1, but it is important to prevent negatively impacting Leadership reporting on “committed vs completed”.

As the new quarter begins, Dana schedules time with the development team to discuss the upcoming features she committed to delivering and prioritizes the stories to be delivered on the first monthly release. The development team begins working on the first 2-week sprint while the testing team begins to build the test automation based on the stories.

At the end of the first sprint, the team submits their changes to the testing team for the testing sprint. However, on the second day of the next sprint, several defects are reported from the testing team, and effort on both teams shifts from development to triage and defect resolution. During the triage process, it’s discovered that the testing and development teams understood the acceptance criteria slightly differently on several stories. Dana schedules a meeting with Jackie to get clarification on how those stories should behave. In the meantime, the development team continues working with the testing team to resolve the defects in the remaining stories.

As the end of the second sprint nears, several of the stories are still impacted by defects and by the pending meeting between the product owner and business user. However, the team begins the process of preparing their change request for the stories that have passed testing for review by the change board and prepares their deploy documentation for the release management team.

After the Change Management Board reviews and approves the release and backout plan, the Release Management team schedules a time on Saturday for the development team to monitor the change after the release team deploys it. During the release, several issues occurred with the release plan that required the development team to make emergency changes to stay within the release window. This turned a scheduled 1-hour process into a 36-hour marathon to patch things so they would be ready by Monday morning.

On Monday morning, Jackie comes to work to try out the new features and is a bit confused. Many things she expected to be delivered have not been. In fact, the stories that have been delivered do not work the way she thought they would. Jackie begins creating defects and eventually emails Dana to request a meeting to find out why so little working software was delivered 2 months after her initial request.

After meeting with Jackie, Dana contacts the development team to find out why the plan failed. Brent, a new developer on the team, suggests that the most effective approach to discovering where things went wrong was to have a postmortem of the last two sprints that included representation from Jackie, Dana, the development team, the testing team, the release management team, and change management to have a holistic view of the entire delivery flow.

Identifying Causes

At the postmortem, Brent facilitates the discussion to find the pain points in the current process. He focuses the attention on “what” and “when” instead of “who”. This result took a team effort by all stakeholders after all.

  • There were several handoffs of information between Jackie, Dana, the development team, and the testing team. Each of those resulted in a loss of context.
  • Even though this work was a higher priority than several items on the previous quarter’s plan, it was scheduled for the next quarter to prevent poor reporting on the “committed vs completed” tracker.
  • The first chance Jackie had to give feedback was after delivery to production.
  • Testing was seen as the responsibility of the testing team. Quality feedback to the development team was delayed until at least a week after development had completed. In addition, each team had an alternative understanding of some of the features.
  • The delivery process was manual and never verified.
  • Because the manual change process required so much time, the team raced to complete work to make the cutoff date without sufficient quality feedback and created more issues as a result.

To begin resolving these issues, they focus on the biggest problems: an unreliable method to deliver changes and poor communication. They decide to make the following changes:

  1. They decide that Jackie and Dana will collaborate on reviewing the backlog frequently and will adjust roadmap priorities as needed rather than strictly adhering to a quarterly plan.
  2. Brent suggests that everyone will have more success if the communication paths are shortened. They decide that Jackie and Dana will meet frequently with the development team and some of the testers to refine work together as a group. They will work together to ensure that there are comprehensive testable acceptance criteria defined for each story and that no work will start on a story unless all stakeholders agree.
  3. The development team will schedule small demos twice a week with the product owner and once per week with Jackie to get more rapid feedback. In addition, effort will be given to improving the testing process over time with the goal that all tests are maintained by the development team. This will prevent two unneeded handoffs, two different misunderstandings of the stories, and the delayed quality feedback from waiting on another team.
  4. Development effort will be diverted to automate the release process and to ensure tests are executed as part of that process. Also, a representative from Change Management will work with the team as they build the delivery automation to ensure it contains the checks they are looking for in CAB and to certify their delivery process can be exempt from manual review.

Iterating to Better

After a couple of sprints of dedicated improvement efforts, things have improved greatly. There is still much work to be done on improving the testing process, but work is better understood and fewer defects are occurring. Jackie has transparency into how work is progressing and can make adjustments to stories as she sees her ideas come to fruition and decides additional changes may be needed to achieve the value she expects. She can also look at the backlog and remove things that are no longer valuable. The development team also has a deeper insight into the business goals Jackie has and begins suggesting improvements to her as well. Dana has a much clearer picture of what the priorities are. As the test and delivery automation improves, she can spend more time on product strategy instead of fighting fires and managing business user expectations. Best of all, the team has an improvement plan that will allow them to begin delivering changes during the working day safely.


Written on January 27, 2021 by Bryan Finster.

Originally published on Medium

Chaos Engineering

Delivering value to the end-users means we need systems that are useful, secure, and resistant to the shark-filled acid bath of the cloud environment. Engineering for chaos is crucial.

There are many resources that discuss the architectural concerns of resiliency engineering. Circuit breakers to gracefully handle the instability of our dependencies. Incrementally reducing feature abilities if dependencies are offline. Switching loads seamlessly between regions if one goes down or SLOs are exceeded. Redundant data persistence, idempotent messages, and etc. While we may be able to predict many failures and make them part of the initial design, we won’t predict for every failure. It requires a resilient product team to deliver resilient solutions.

What is a resilient team? It is a team that can continue to deliver value while dealing with the chaos of the real world. One of the principle practices of DevOps is that the team contains all of the capabilities required to deliver and operate their product. However, it’s possible to assemble a cross-functional team that’s brittle and will fall apart as soon as anything unexpected happens. Here are a few team anti-patterns that will put your ability to meet business goals at risk.

Individual Ownership

This anti-pattern is where a developer is the primary owner of some part of the system. This could be caused by a manager looking for ways to highlight someone for a promotion, someone joining the team who brings a new component with them, someone having expertise in a particular framework, or many other reasons. It’s tempting to do this. If we have a person who is an expert at something and we have deadlines, the obvious solution is to assign work based on expertise. This creates several large problems.

  • What is the quality process? If only a small subset of the team understands something, how will the rest of the team review for quality?
  • Things deliver slowly because no one else really understands the code or the business problem it solves. Therefore, no one on the team can help without onboarding.
  • When the “owner” goes on vacation, has a family emergency, or wins the lottery, how will development continue in their absence?

Tactical decisions based on best-case scenarios and hope can succeed as long as nothing goes wrong. Business strategy requires embracing reality. Terminate knowledge silos with extreme prejudice.

The Change Agent

Improvement rarely starts as an overall team effort. However, improvement of daily work is a business deliverable. If we are not continuously improving, we are continuously degrading. There is no steady-state position. If we have a person on the team who is passionate about improving things, we cannot depend on that passion long-term as the driver of improvement.

  • Passion burns out if there is no motion or active support.
  • As in the “single owner” anti-pattern, the improvement will depend on the presence of that contributor. What happens when they seek greener pastures.

Leverage the passion for improvement to infect the team, but embed it into the team’s culture by incentivizing it as we would any other value delivery. Grow more change agents.

Assigned roles

Assigned roles and titles awesome for HR, but do not add value to the business. Product teams have one role; deliver business value and continuously improve how we do that.

  • Assigning functional roles on the team leads to “not my job” and extended wait times while hand-offs occur within the team.
  • If a link in the chain is broken due to the chaos of the real world, then value stops.
  • If the flow of work overwhelms the capacity of one of the assigned roles, then lead times extend, and value is lost due to delay.

It’s fine to assemble a group of specialists to develop an MVP, but the complexity debt in the application and in the team will steadily increase if things remain specialized. Over the long term, that structure will drive you into a ditch. Optimize for sustainable value delivery and impact to the bottom line, not HR concerns.

These are all common issues on teams that are not deliberately architected to deliver resilient applications. When short term deliverables have more focus than long term strategy, we’ll miss our goals. Plan for chaos and architect teams for reality.


Written on January 25, 2021 by Bryan Finster.

Originally published on Medium

Continuous Delivery FAQ

There are many misconceptions about CD. There are also many things about CD that are not obvious on the surface. If you are “CD curious”, perhaps this will help.

“CI/CD or CD?”

It’s just “CD”. Just as DevOps encompasses security, business, compliance, user experience, etc., CD encompasses the entire development process.

“There is no objective definition for CI”

There is an objective definition. Some may believe that pulling updates from the trunk daily but only integrating changes after several days of work to make sure the change is “feature complete” is CI. People believe all sorts of things that aren’t true. Believing really hard doesn’t make it more true. The point of CI is team communication. The code integrates very frequently and is tested together. This allows everyone to see everyone else’s changes. It reduces the problems with dependency between changes, code conflicts, etc. Smaller is better.

“We automated the build. Are we doing CD?”

Build and deploy automation are important. Test automation is even more important. It takes people, process, and automation to execute CD. Automation is about 10% of the CD problem though.

Continuous delivery is continuous product development and continuous quality feedback. The robots enable us to standardize and accelerate delivery to reduce the cost and improve the safety of change. This makes it viable for us to deliver very small changes to get feedback on quality immediately. People frequently underestimate the definition of “small”. We aren’t talking epics, features, or even stories. We are talking hours or minutes worth of work, not days or weeks. A CD pipeline includes the robots, but it starts with the value proposition and ends when we get feedback from production on the actual value delivered so that we can make decisions about future value propositions. This takes teamwork from everyone aligned to the product flow and unrelenting discipline for maintaining and improving quality feedback loops.

“CD is just for unicorn startups?”

CD is a quality process, not an edge case used by dotComs iterating quickly on MVPs. Many startups will grind to a halt and fail because they don’t include “how will we reliably deliver future changes?” in their business plans. Hopefully, they get enough funding to clear the massive tech debt before that occurs.

The reasons for delivering very frequently are:

  • Ensure that we can deliver patches quickly and safely in an emergency.
  • Establish an efficient and effective quality signal to reduce mean time to detect.
  • Reduce the size of changes to reduce the size of the defects in each change.
  • Reduce the cost of change to enable more freedom to try new ideas.

CD, when used to drive down batch size and learn from production, acts as a forcing function for stability, efficiency, effective value delivery, and improved quality of life.

“We will focus on CD after we deliver these features”

There’s really no point in building a feature if you cannot reliably test and deliver the feature to validate the value. Also, you have no idea if your tests are valid until you deliver and find out.

  • The requirements are probably wrong
  • We probably misunderstood them
  • They will probably change before we deliver

How much investment do you want to make in development before you find out how many of the above are true?

The correct order of operations is:

  1. Pipeline to production
  2. Production delivery of “hello world” to validate the pipeline
  3. Next change.

1 & 2 should occur on day 1.

“We’ll need to hire smarter people”

No, CD is the tool that grows better engineers and better teams. Solving the problem of “why can’t we deliver this change safely to production today?” in your context is an engineering problem that takes teamwork and disciplined development. If this is the primary focus then skills such as effective use of feature flagging, continuous integration, developer-driven testing, & etc. will grow rapidly. It also improves morale due to the reduced stress of repetitive toil and Fear Driven Development.

“Our users don’t want changes that often”

I assure you that when you have an impacting incident that your users want changes that often. There’s a big difference between generally exposing new features daily and delivering changes daily to verify they do not break. Again, this is a quality process, not a feature delivery process. We need quality feedback from continuous testing in production instead of hoping our next big release doesn’t break anything. Your next big release will break something. See point 3 above.

“We don’t believe these wild claims about CD”

That’s pretty common. Most people don’t understand how applying discipline to the development process and focusing on shrinking change-set sizes can make such a dramatic improvement to business outcomes and quality of life. Those of us who live that are trying daily to show that to you. Most of us aren’t paid consultants, tool vendors, or service vendors. We are telling you this because we want your lives to be better too and we want to maintain our quality of life if we join your organization.

“How hard is it to start?”

That very much depends on how teams are structured and how applications are structured. I recommend learning about Conway’s Law, Domain-Driven Design, and reading “Team Topologies”. For a team already organized to be small and product-focused, it will take a few months, given the right support and a knowledgeable person or two. For larger, legacy systems it will take the same broader organizational changes your competitors are working on today. The best time to plant a tree is 20 years ago. The second best time is today.

“What happens to teams who are prevented from improving CD?”

Once developers discover that their life is getting better, they will not want to stop. If you are in a management role for a team that is focusing on CD and then decide to send the team on a death march that prevents them from sustainably improving or decide to impose processes that prevent CD, the good ones will resist you by voting with their feet. You’ll be left with the below-average developers who didn’t fully embrace improvement. Good luck with your goals.

I will continue adding to this list as I think about other common questions. Feel free to check back or to comment with questions, challenges, or arguments you have where CD isn’t a valid process.

Changelog

  • Jan 23 2021: Initial commit
  • Mar 28 2021: Added “We automated the build. We are doing CD.”

Written on January 23, 2021 by Bryan Finster.

Originally published on Medium

The Value of Code Coverage

Code coverage is the measure of the amount of code being executed by tests. It’s an easy number to measure and a minimum value is frequently set as a goal. What is the actual value of code coverage?

True story, I was once in an organization where management told us that one of our annual goals was to ensure all applications had 90% code coverage. See, we were just starting to push testing in the area and they wanted to track our progress to make sure testing was happening. It was an interesting social experiment that I frequently see repeated in the industry. I learned a bit about people and measuring goals from that.

What was the outcome? Test coverage went up, naturally.

Let’s try this out. I’ve been given the task to write a function that will add two whole numbers. I have requirements that it should return the sum of two whole numbers but if a decimal value or a non-number is passed, then I need to return the JavaScript value for “Not a Number”. Should be easy.


<!DOCTYPE html>
<html lang="en" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark" data-a11y-animated-images="system">
  <head>
    <meta charset="utf-8">
  <link rel="dns-prefetch" href="https://github.githubassets.com">
  <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
  <link rel="preconnect" href="https://github.githubassets.com" crossorigin>
  <link rel="preconnect" href="https://avatars.githubusercontent.com">

  <link crossorigin="anonymous" media="all" integrity="sha512-ksfTgQOOnE+FFXf+yNfVjKSlEckJAdufFIYGK7ZjRhWcZgzAGcmZqqArTgMLpu90FwthqcCX4ldDgKXbmVMeuQ==" rel="stylesheet" href="https://github.githubassets.com/assets/light-92c7d381038e.css" /><link crossorigin="anonymous" media="all" integrity="sha512-1KkMNn8M/al/dtzBLupRwkIOgnA9MWkm8oxS+solP87jByEvY/g4BmoxLihRogKcX1obPnf4Yp7dI0ZTWO+ljg==" rel="stylesheet" href="https://github.githubassets.com/assets/dark-d4a90c367f0c.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" integrity="sha512-cZa7DZqvMBwD236uzEunO/G1dvw8/QftyT2UtLWKQFEy0z0eq0R5WPwqVME+3NSZG1YaLJAaIqtU+m0zWf/6SQ==" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-7196bb0d9aaf.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" integrity="sha512-WVoKqJ4y1nLsdNH4RkRT5qrM9+n9RFe1RHSiTnQkBf5TSZkJEc9GpLpTIS7T15EQaUQBJ8BwmKvwFPVqfpTEIQ==" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-595a0aa89e32.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" integrity="sha512-XpAMBMSRZ6RTXgepS8LjKiOeNK3BilRbv8qEiA/M3m+Q4GoqxtHedOI5BAZRikCzfBL4KWYvVzYZSZ8Gp/UnUg==" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-5e900c04c491.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" integrity="sha512-3HF2HZ4LgEIQm77yOzoeR20CX1n2cUQlcywscqF4s+5iplolajiHV7E5ranBwkX65jN9TNciHEVSYebQ+8xxEw==" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-dc71761d9e0b.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" integrity="sha512-+J8j3T0kbK9/sL3zbkCfPtgYcRD4qQfRbT6xnfOrOTjvz4zhr0M7AXPuE642PpaxGhHs1t77cTtieW9hI2K6Gw==" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-f89f23dd3d24.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" integrity="sha512-AQeAx5wHQAXNf0DmkvVlHYwA3f6BkxunWTI0GGaRN57GqD+H9tW8RKIKlopLS0qGaC54seFsPc601GDlqIuuHg==" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-010780c79c07.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" integrity="sha512-+u5pmgAE0T03d/yI6Ha0NWwz6Pk0W6S6WEfIt8veDVdK8NTjcMbZmQB9XUCkDlrBoAKkABva8HuGJ+SzEpV1Uw==" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-faee699a0004.css" />

    <link crossorigin="anonymous" media="all" integrity="sha512-EAhBCLIJ/pXHG3Y6yQhs9s53SHV80sjJ+yCwlQtfv7LaVkD+VoEuZBZ5betQJFUNj/5qBSfZk5GFtazEDzWLAg==" rel="stylesheet" href="https://github.githubassets.com/assets/primer-10084108b209.css" />
    <link crossorigin="anonymous" media="all" integrity="sha512-d9qxvYDCx8mjHvFPT2FYDW0IbQ4NBX40LnnapTFQqQi9tlkpGN2g9gpwd+ZPLGZqQfITM5dtOke5EP7y8FOGTQ==" rel="stylesheet" href="https://github.githubassets.com/assets/global-77dab1bd80c2.css" />
    <link crossorigin="anonymous" media="all" integrity="sha512-QVHRvU+lsiBTT0dePas9QeGUp/AUz8nmg5MOU5vTdzbvu8eEamPp51Zk/H9qsm70vBgDWe3fXKUDoxAs3pOe0g==" rel="stylesheet" href="https://github.githubassets.com/assets/github-4151d1bd4fa5.css" />


  <script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-9kIUkROoz3qRLPYpBVPXcAwu2mSRB3l2ykBnEefEK6iqM5RHKPSsZ76/ZXyOC5mGuiFc47XGiwn3pg8E3tLqYg==" src="https://github.githubassets.com/assets/runtime-f642149113a8.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-4ytm+HbTBiWJpHp1HiEdx5ZV6Wms2WGHPLh4+ACDwBgU/ua/ziz0Qk3iPD4m1rBpCLukqjwEemWGl2xyvPnGog==" src="https://github.githubassets.com/assets/environment-e32b66f876d3.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-io+1MvgXPXTw8Kp4eOdNMJl8uGASuw8VfTY5VeIFETaAknimWi8GoxggMEeQ6mq0de4Dest4iIJ/9gUbCo0hgw==" src="https://github.githubassets.com/assets/vendors-node_modules_selector-observer_dist_index_esm_js-8a8fb532f817.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-Es25N4GyPa8Yfp5wpahoe5b2fyPtkRMyR6mKIXyCJC0ocqQazeWvxhGZhx3StRxOfqDfHDR5SS35u/R3Wux6Cg==" src="https://github.githubassets.com/assets/vendors-node_modules_delegated-events_dist_index_js-node_modules_github_details-dialog-elemen-63debe-12cdb93781b2.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-lmiecOIgf+hakg5oKMNM7grVhEDyPoIrT39Px448JJH5PSAaK21PH0Twgyz5O5oi8+dnlLr3Jt8bBCtAcpNdRw==" src="https://github.githubassets.com/assets/vendors-node_modules_github_filter-input-element_dist_index_js-node_modules_github_remote-inp-c7e9ed-96689e70e220.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-y67eNkVaNK4RguUGcHOvIbHFlgf1Qje+LDdjVw2eFuuvBOqta2GePz/CwoLIR/PJhhRAj5RPGxCWoomnimSw6w==" src="https://github.githubassets.com/assets/vendors-node_modules_github_catalyst_lib_index_js-node_modules_github_time-elements_dist_index_js-cbaede36455a.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-DE9GK/TS3Hf/dJfP1GMiyjvsGRnpR4A1eGH64s0Ot7Vy7Tj7/aD1d5EOMWHFWQXlXy/ZSwu5MHInG6RZN31bMA==" src="https://github.githubassets.com/assets/vendors-node_modules_github_file-attachment-element_dist_index_js-node_modules_primer_view-co-52e104-0c4f462bf4d2.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-i0Ha1jdl+Ze1GOoj8XJ4FmsHvIvxukCOi0Om2NK2574NSfisYFHJTigepoBa0Ft+u1d2T9Lc3C60Pn9ma+gAng==" src="https://github.githubassets.com/assets/github-elements-8b41dad63765.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-IkxSyqUlZiwuThbg3rOi8RD6r5e0m9OdtUdc+YAewC823+jpgEMlcP/eIQFjkFOJlq9yZKMKlobrTGIPfBuDEQ==" src="https://github.githubassets.com/assets/element-registry-224c52caa525.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-uo73yUZcm4EicwjSbfxFZcKfjWniOxhLBp+q1n7IFRfutFM6/lzbQMgD0Xrxp7QD1HzqdvrV8UclPhi3mEOyzQ==" src="https://github.githubassets.com/assets/vendors-node_modules_lit-html_lit-html_js-ba8ef7c9465c.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-5XdknqL9iTuaAhbOnKJDHf+5bJReOYDwlz5p2mW+SAVfab4RN6AfIliqtPwheKld40DrhPs47We/mT5nlZq2Sg==" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_github_catalyst_lib_index_-87b1b3-e577649ea2fd.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-0r1nf/rfPz54kyePp4f63bcPxkFo7wyaUZJD/SwIVDK3q0WzurAK9ydOm88tzKtPJm8xWI0Vo25NyCfecwxJ9g==" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_github_hotkey_dist_index-9f48bd-d2bd677ffadf.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-VkNE0AbPkr2TU6VW7p0hdWWLXTz4KsVdHZpIaR/Y44T9h5qJ4Td+hpFVZOVbfxiTOJVh7lyd2ICk/CNo4MI+Vw==" src="https://github.githubassets.com/assets/vendors-node_modules_github_paste-markdown_dist_index_esm_js-node_modules_github_quote-select-f71f15-564344d006cf.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-3H2GHBTxx8exTC9xT8LEQcEEEsFBwWEQUM0D9zs5SDtw6JjEZlW0j6JB1GG0/zgLnqcJaPRodIVuEKyDWxjugQ==" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_pjax_ts-dc7d861c14f1.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-1VTsfTthjXvPtrjOvr3XUFjwRwpH5AloYdd7+mwF6+/CoUCyh9/ubBqMz7E7Mt87Pn2kihXHOBfRUWg1WZLkXw==" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_keyboard-shortcuts-helper_ts-app_assets_modules_github_be-af52ef-d554ec7d3b61.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-NWll0aK8DvxvsFkfTc5M1jKhFioP/Mm0rUSzEJhQZkYIEMS/gRGK3kaKhokckGVFgzmdEdawcVw4SqVHJ8+vGg==" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_details_ts-app_assets_modules_github_behaviors_include-fr-a5a4c7-356965d1a2bc.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-3pHFCSyIm9PY6CD8Dlu2uCTdRoBlrdLy4kmwWoQk/7xw9hFIGHYbnS94dSf/zyQAqWCkUNU0Z2DBt5t0tPW9zg==" src="https://github.githubassets.com/assets/behaviors-de91c5092c88.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-rLnbCtiKIpRtm1jgAz913tDImW3g8G+FLRMBIMwrkZ33pO8PMzWDG4m8Zl0uIIs10hCyFr6Dc8s6VDC200S9lg==" src="https://github.githubassets.com/assets/vendors-node_modules_delegated-events_dist_index_js-node_modules_github_catalyst_lib_index_js-01c6c51-acb9db0ad88a.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-paDaHAe8uAMftTo7ooywENAI3NeRIHNk0IxXV2vJxs3L5/F7hiGDPCDCq2YWOgshQV/AyMjc4bb3TzU9o/+BWQ==" src="https://github.githubassets.com/assets/notifications-global-a5a0da1c07bc.js"></script>
    <script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-7D+usKScZ9o6LbSTCUPAPzqQibion+w0hRJ7z5q49c1OgKNOZmuO1GTr7MZRQE1sxG4BT78ZSW97mC8BUOfXIQ==" src="https://github.githubassets.com/assets/vendors-node_modules_github_clipboard-copy-element_dist_index_esm_js-node_modules_github_remo-9d7385-ec3faeb0a49c.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-3o22iDcc4unX7ots5Oy7lxj69diILvwJv2EPBu169NUyDt3jPIAkRNsz/UrN9QBJK6r6b5kSMhTb/pkMz4lwfQ==" src="https://github.githubassets.com/assets/app_assets_modules_github_diffs_blob-lines_ts-app_assets_modules_github_diffs_linkable-line-n-f314c3-de8db688371c.js"></script>
<script crossorigin="anonymous" defer="defer" type="application/javascript" integrity="sha512-D0asfFmKud7L80V75oA7aLwhbK2pgUagGaOwDc6lE12o0V8fzEkp5PzWoOrwr0nB1vnmJrXnYkrW98nBUifJpg==" src="https://github.githubassets.com/assets/gist-0f46ac7c598a.js"></script>

  <title>Adding two numbers badly · GitHub</title>



  <meta name="request-id" content="FC75:5A4D:57E92D:9372D6:62B5A004" data-pjax-transient="true"/><meta name="html-safe-nonce" content="482e002e44368ff2ba1dc7e3ac935d2fc2f814668fcae346a0c73e5398a23db6" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6Imh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vYmRmaW5zdC81MWUyODFkZDdhZDk2ZjJjMjU5MDM1OWFkNzcwNTQxZiNmaWxlLWFkZHdob2xlbnVtYmVycy1qcy9yYXciLCJyZXF1ZXN0X2lkIjoiRkM3NTo1QTREOjU3RTkyRDo5MzcyRDY6NjJCNUEwMDQiLCJ2aXNpdG9yX2lkIjoiMjI2MzUxMDY3OTMyOTAyMTk1NiIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9" data-pjax-transient="true"/><meta name="visitor-hmac" content="697abf73ffc5de2dff6cef48fac8c362714b18e6afafc656f795be1327ad1f1a" data-pjax-transient="true"/>

  <meta name="github-keyboard-shortcuts" content="" data-pjax-transient="true" />


  <meta name="selected-link" value="gist_code" data-pjax-transient>

    <meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY">
  <meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
  <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
  <meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc">

<meta name="octolytics-url" content="https://collector.github.com/github/collect" />

  <meta name="analytics-location" content="/&lt;user-name&gt;/&lt;gist-id&gt;" data-pjax-transient="true" />



    <meta name="octolytics-dimension-public" content="true" /><meta name="octolytics-dimension-gist_id" content="107415153" /><meta name="octolytics-dimension-gist_name" content="51e281dd7ad96f2c2590359ad770541f" /><meta name="octolytics-dimension-anonymous" content="false" /><meta name="octolytics-dimension-owner_id" content="8195127" /><meta name="octolytics-dimension-owner_login" content="bdfinst" /><meta name="octolytics-dimension-forked" content="false" />

    <meta name="user-login" content="">

    <meta name="viewport" content="width=device-width">

      <meta name="description" content="Adding two numbers badly. GitHub Gist: instantly share code, notes, and snippets.">
      <link rel="search" type="application/opensearchdescription+xml" href="/opensearch-gist.xml" title="Gist">
    <link rel="fluid-icon" href="https://gist.github.com/fluidicon.png" title="GitHub">
    <meta property="fb:app_id" content="1401488693436528">
    <meta name="apple-itunes-app" content="app-id=1477376905" />
      <meta name="twitter:thumbnail:src" content="https://github.githubassets.com/img/modules/gists/gist-og-image.png" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="Adding two numbers badly" /><meta name="twitter:description" content="Adding two numbers badly. GitHub Gist: instantly share code, notes, and snippets." />
      <meta property="og:image" content="https://github.githubassets.com/img/modules/gists/gist-og-image.png" /><meta property="og:thumbnail:alt" content="Adding two numbers badly. GitHub Gist: instantly share code, notes, and snippets." /><meta property="og:site_name" content="Gist" /><meta property="og:type" content="article" /><meta property="og:title" content="Adding two numbers badly" /><meta property="og:url" content="https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f" /><meta property="og:description" content="Adding two numbers badly. GitHub Gist: instantly share code, notes, and snippets." /><meta property="article:author" content="262588213843476" /><meta property="article:publisher" content="262588213843476" />

    <link rel="assets" href="https://github.githubassets.com/">


        <meta name="hostname" content="gist.github.com">

        <meta name="expected-hostname" content="gist.github.com">

    <meta name="enabled-features" content="IMAGE_METRIC_TRACKING">

  <meta http-equiv="x-pjax-version" content="1233cc4ecf30b1a22963bf1da05b8083562ac7c876fd0dd89aaa03240cf87750" data-turbo-track="reload">
  <meta http-equiv="x-pjax-csp-version" content="485d6a5ccbb1eeae9c86b616b4870b531f6f458e8bd5c309c40280dc4f51defb" data-turbo-track="reload">
  <meta http-equiv="x-pjax-css-version" content="4b86fabb8e62df07ed1a2106c1c4cb847cbf31bbdf1fc1eae7a0adac45fc08cc" data-turbo-track="reload">
  <meta http-equiv="x-pjax-js-version" content="6b8a04c60e96f975d03f9597fbe8ae4d41e671a065954ef1bf05b6a0b17c10fd" data-turbo-track="reload">

  <meta name="turbo-cache-control" content="no-preview"></meta>

      <link href="/bdfinst.atom" rel="alternate" title="atom" type="application/atom+xml">

  <link crossorigin="anonymous" media="all" integrity="sha512-sh2yjS+AxvEdrKln9ht0mBnnVTVeQKatYC0mwWIVYyP/6rzOH6qLj5XypFJ+dAFa2BOCYc0Yhpq9iw4KtVFR0g==" rel="stylesheet" href="https://github.githubassets.com/assets/gist-b21db28d2f80.css" />

  <meta name="turbo-body-classes" content="logged-out env-production page-responsive">

  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">

  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">

  <meta name="browser-optimizely-client-errors-url" content="https://api.github.com/_private/browser/optimizely_client/errors">

  <link rel="mask-icon" href="https://github.githubassets.com/pinned-octocat.svg" color="#000000">
  <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
  <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg">

<meta name="theme-color" content="#1e2327">
<meta name="color-scheme" content="light dark" />

  </head>

  <body class="logged-out env-production page-responsive" style="word-wrap: break-word;">


    <div class="position-relative js-header-wrapper ">
      <a href="#start-of-content" class="px-2 py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a>
      <span data-view-component="true" class="progress-pjax-loader js-pjax-loader-bar Progress position-fixed width-full">
    <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span>
</span>


          <div class="Header js-details-container Details flex-wrap flex-md-nowrap p-responsive" role="banner" >
  <div class="Header-item d-none d-md-flex">
    <a class="Header-link" data-hotkey="g d" aria-label="Gist Homepage " href="/">
  <svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github v-align-middle d-inline-block d-md-none">
    <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
  <svg aria-hidden="true" height="24" viewBox="0 0 45 16" version="1.1" width="67" data-view-component="true" class="octicon octicon-logo-github v-align-middle d-none d-md-inline-block">
    <path fill-rule="evenodd" d="M18.53 12.03h-.02c.009 0 .015.01.024.011h.006l-.01-.01zm.004.011c-.093.001-.327.05-.574.05-.78 0-1.05-.36-1.05-.83V8.13h1.59c.09 0 .16-.08.16-.19v-1.7c0-.09-.08-.17-.16-.17h-1.59V3.96c0-.08-.05-.13-.14-.13h-2.16c-.09 0-.14.05-.14.13v2.17s-1.09.27-1.16.28c-.08.02-.13.09-.13.17v1.36c0 .11.08.19.17.19h1.11v3.28c0 2.44 1.7 2.69 2.86 2.69.53 0 1.17-.17 1.27-.22.06-.02.09-.09.09-.16v-1.5a.177.177 0 00-.146-.18zM42.23 9.84c0-1.81-.73-2.05-1.5-1.97-.6.04-1.08.34-1.08.34v3.52s.49.34 1.22.36c1.03.03 1.36-.34 1.36-2.25zm2.43-.16c0 3.43-1.11 4.41-3.05 4.41-1.64 0-2.52-.83-2.52-.83s-.04.46-.09.52c-.03.06-.08.08-.14.08h-1.48c-.1 0-.19-.08-.19-.17l.02-11.11c0-.09.08-.17.17-.17h2.13c.09 0 .17.08.17.17v3.77s.82-.53 2.02-.53l-.01-.02c1.2 0 2.97.45 2.97 3.88zm-8.72-3.61h-2.1c-.11 0-.17.08-.17.19v5.44s-.55.39-1.3.39-.97-.34-.97-1.09V6.25c0-.09-.08-.17-.17-.17h-2.14c-.09 0-.17.08-.17.17v5.11c0 2.2 1.23 2.75 2.92 2.75 1.39 0 2.52-.77 2.52-.77s.05.39.08.45c.02.05.09.09.16.09h1.34c.11 0 .17-.08.17-.17l.02-7.47c0-.09-.08-.17-.19-.17zm-23.7-.01h-2.13c-.09 0-.17.09-.17.2v7.34c0 .2.13.27.3.27h1.92c.2 0 .25-.09.25-.27V6.23c0-.09-.08-.17-.17-.17zm-1.05-3.38c-.77 0-1.38.61-1.38 1.38 0 .77.61 1.38 1.38 1.38.75 0 1.36-.61 1.36-1.38 0-.77-.61-1.38-1.36-1.38zm16.49-.25h-2.11c-.09 0-.17.08-.17.17v4.09h-3.31V2.6c0-.09-.08-.17-.17-.17h-2.13c-.09 0-.17.08-.17.17v11.11c0 .09.09.17.17.17h2.13c.09 0 .17-.08.17-.17V8.96h3.31l-.02 4.75c0 .09.08.17.17.17h2.13c.09 0 .17-.08.17-.17V2.6c0-.09-.08-.17-.17-.17zM8.81 7.35v5.74c0 .04-.01.11-.06.13 0 0-1.25.89-3.31.89-2.49 0-5.44-.78-5.44-5.92S2.58 1.99 5.1 2c2.18 0 3.06.49 3.2.58.04.05.06.09.06.14L7.94 4.5c0 .09-.09.2-.2.17-.36-.11-.9-.33-2.17-.33-1.47 0-3.05.42-3.05 3.73s1.5 3.7 2.58 3.7c.92 0 1.25-.11 1.25-.11v-2.3H4.88c-.11 0-.19-.08-.19-.17V7.35c0-.09.08-.17.19-.17h3.74c.11 0 .19.08.19.17z"></path>
</svg>
  <svg aria-hidden="true" height="24" viewBox="0 0 25 16" version="1.1" width="37" data-view-component="true" class="octicon octicon-logo-gist v-align-middle d-none d-md-inline-block">
    <path fill-rule="evenodd" d="M4.7 8.73h2.45v4.02c-.55.27-1.64.34-2.53.34-2.56 0-3.47-2.2-3.47-5.05 0-2.85.91-5.06 3.48-5.06 1.28 0 2.06.23 3.28.73V2.66C7.27 2.33 6.25 2 4.63 2 1.13 2 0 4.69 0 8.03c0 3.34 1.11 6.03 4.63 6.03 1.64 0 2.81-.27 3.59-.64V7.73H4.7v1zm6.39 3.72V6.06h-1.05v6.28c0 1.25.58 1.72 1.72 1.72v-.89c-.48 0-.67-.16-.67-.7v-.02zm.25-8.72c0-.44-.33-.78-.78-.78s-.77.34-.77.78.33.78.77.78.78-.34.78-.78zm4.34 5.69c-1.5-.13-1.78-.48-1.78-1.17 0-.77.33-1.34 1.88-1.34 1.05 0 1.66.16 2.27.36v-.94c-.69-.3-1.52-.39-2.25-.39-2.2 0-2.92 1.2-2.92 2.31 0 1.08.47 1.88 2.73 2.08 1.55.13 1.77.63 1.77 1.34 0 .73-.44 1.42-2.06 1.42-1.11 0-1.86-.19-2.33-.36v.94c.5.2 1.58.39 2.33.39 2.38 0 3.14-1.2 3.14-2.41 0-1.28-.53-2.03-2.75-2.23h-.03zm8.58-2.47v-.86h-2.42v-2.5l-1.08.31v2.11l-1.56.44v.48h1.56v5c0 1.53 1.19 2.13 2.5 2.13.19 0 .52-.02.69-.05v-.89c-.19.03-.41.03-.61.03-.97 0-1.5-.39-1.5-1.34V6.94h2.42v.02-.01z"></path>
</svg>
</a>
  </div>

  <div class="Header-item d-md-none">
    <button aria-label="Toggle navigation" aria-expanded="false" type="button" data-view-component="true" class="Header-link js-details-target btn-link">  <svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24" data-view-component="true" class="octicon octicon-three-bars">
    <path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path>
</svg>

</button>  </div>

  <div class="Header-item Header-item--full js-site-search flex-column flex-md-row width-full flex-order-2 flex-md-order-none mr-0 mr-md-3 mt-3 mt-md-0 Details-content--hidden-not-important d-md-flex">
      <div class="header-search flex-self-stretch flex-md-self-auto mr-0 mr-md-3 mb-3 mb-md-0">
  <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="position-relative js-quicksearch-form" role="search" aria-label="Site" data-turbo="false" action="/search" accept-charset="UTF-8" method="get">
    <div class="header-search-wrapper form-control input-sm js-chromeless-input-container">
      <input type="text"
        class="form-control input-sm js-site-search-focus header-search-input"
        data-hotkey="s,/"
        name="q"
        aria-label="Search"
        placeholder="Search…"
        autocorrect="off"
        autocomplete="off"
        autocapitalize="off">
    </div>

</form></div>

    <nav aria-label="Global" class="d-flex flex-column flex-md-row flex-self-stretch flex-md-self-auto">
  <a class="Header-link mr-0 mr-md-3 py-2 py-md-0 border-top border-md-top-0 border-white-fade" data-ga-click="Header, go to all gists, text:all gists" href="/discover">All gists</a>

  <a class="Header-link mr-0 mr-md-3 py-2 py-md-0 border-top border-md-top-0 border-white-fade" data-ga-click="Header, go to GitHub, text:Back to GitHub" href="https://github.com">Back to GitHub</a>

    <a class="Header-link d-block d-md-none mr-0 mr-md-3 py-2 py-md-0 border-top border-md-top-0 border-white-fade" data-ga-click="Header, sign in" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f7b345f75fbc3f75d48e49b9c24023a79cd4a63dc63c42874f7b729e346b2282" href="https://gist.github.com/auth/github?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">
      Sign in
</a>
      <a class="Header-link d-block d-md-none mr-0 mr-md-3 py-2 py-md-0 border-top border-md-top-0 border-white-fade" data-ga-click="Header, sign up" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="78cc466708732ab6b09c4650ed14a2f2767b5b61546c1253b092de4b8fd8d380" href="/join?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f&amp;source=header-gist">
        Sign up
</a></nav>

  </div>

  <div class="Header-item Header-item--full flex-justify-center d-md-none position-relative">
    <a class="Header-link" data-hotkey="g d" aria-label="Gist Homepage " href="/">
  <svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github v-align-middle d-inline-block d-md-none">
    <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
  <svg aria-hidden="true" height="24" viewBox="0 0 45 16" version="1.1" width="67" data-view-component="true" class="octicon octicon-logo-github v-align-middle d-none d-md-inline-block">
    <path fill-rule="evenodd" d="M18.53 12.03h-.02c.009 0 .015.01.024.011h.006l-.01-.01zm.004.011c-.093.001-.327.05-.574.05-.78 0-1.05-.36-1.05-.83V8.13h1.59c.09 0 .16-.08.16-.19v-1.7c0-.09-.08-.17-.16-.17h-1.59V3.96c0-.08-.05-.13-.14-.13h-2.16c-.09 0-.14.05-.14.13v2.17s-1.09.27-1.16.28c-.08.02-.13.09-.13.17v1.36c0 .11.08.19.17.19h1.11v3.28c0 2.44 1.7 2.69 2.86 2.69.53 0 1.17-.17 1.27-.22.06-.02.09-.09.09-.16v-1.5a.177.177 0 00-.146-.18zM42.23 9.84c0-1.81-.73-2.05-1.5-1.97-.6.04-1.08.34-1.08.34v3.52s.49.34 1.22.36c1.03.03 1.36-.34 1.36-2.25zm2.43-.16c0 3.43-1.11 4.41-3.05 4.41-1.64 0-2.52-.83-2.52-.83s-.04.46-.09.52c-.03.06-.08.08-.14.08h-1.48c-.1 0-.19-.08-.19-.17l.02-11.11c0-.09.08-.17.17-.17h2.13c.09 0 .17.08.17.17v3.77s.82-.53 2.02-.53l-.01-.02c1.2 0 2.97.45 2.97 3.88zm-8.72-3.61h-2.1c-.11 0-.17.08-.17.19v5.44s-.55.39-1.3.39-.97-.34-.97-1.09V6.25c0-.09-.08-.17-.17-.17h-2.14c-.09 0-.17.08-.17.17v5.11c0 2.2 1.23 2.75 2.92 2.75 1.39 0 2.52-.77 2.52-.77s.05.39.08.45c.02.05.09.09.16.09h1.34c.11 0 .17-.08.17-.17l.02-7.47c0-.09-.08-.17-.19-.17zm-23.7-.01h-2.13c-.09 0-.17.09-.17.2v7.34c0 .2.13.27.3.27h1.92c.2 0 .25-.09.25-.27V6.23c0-.09-.08-.17-.17-.17zm-1.05-3.38c-.77 0-1.38.61-1.38 1.38 0 .77.61 1.38 1.38 1.38.75 0 1.36-.61 1.36-1.38 0-.77-.61-1.38-1.36-1.38zm16.49-.25h-2.11c-.09 0-.17.08-.17.17v4.09h-3.31V2.6c0-.09-.08-.17-.17-.17h-2.13c-.09 0-.17.08-.17.17v11.11c0 .09.09.17.17.17h2.13c.09 0 .17-.08.17-.17V8.96h3.31l-.02 4.75c0 .09.08.17.17.17h2.13c.09 0 .17-.08.17-.17V2.6c0-.09-.08-.17-.17-.17zM8.81 7.35v5.74c0 .04-.01.11-.06.13 0 0-1.25.89-3.31.89-2.49 0-5.44-.78-5.44-5.92S2.58 1.99 5.1 2c2.18 0 3.06.49 3.2.58.04.05.06.09.06.14L7.94 4.5c0 .09-.09.2-.2.17-.36-.11-.9-.33-2.17-.33-1.47 0-3.05.42-3.05 3.73s1.5 3.7 2.58 3.7c.92 0 1.25-.11 1.25-.11v-2.3H4.88c-.11 0-.19-.08-.19-.17V7.35c0-.09.08-.17.19-.17h3.74c.11 0 .19.08.19.17z"></path>
</svg>
  <svg aria-hidden="true" height="24" viewBox="0 0 25 16" version="1.1" width="37" data-view-component="true" class="octicon octicon-logo-gist v-align-middle d-none d-md-inline-block">
    <path fill-rule="evenodd" d="M4.7 8.73h2.45v4.02c-.55.27-1.64.34-2.53.34-2.56 0-3.47-2.2-3.47-5.05 0-2.85.91-5.06 3.48-5.06 1.28 0 2.06.23 3.28.73V2.66C7.27 2.33 6.25 2 4.63 2 1.13 2 0 4.69 0 8.03c0 3.34 1.11 6.03 4.63 6.03 1.64 0 2.81-.27 3.59-.64V7.73H4.7v1zm6.39 3.72V6.06h-1.05v6.28c0 1.25.58 1.72 1.72 1.72v-.89c-.48 0-.67-.16-.67-.7v-.02zm.25-8.72c0-.44-.33-.78-.78-.78s-.77.34-.77.78.33.78.77.78.78-.34.78-.78zm4.34 5.69c-1.5-.13-1.78-.48-1.78-1.17 0-.77.33-1.34 1.88-1.34 1.05 0 1.66.16 2.27.36v-.94c-.69-.3-1.52-.39-2.25-.39-2.2 0-2.92 1.2-2.92 2.31 0 1.08.47 1.88 2.73 2.08 1.55.13 1.77.63 1.77 1.34 0 .73-.44 1.42-2.06 1.42-1.11 0-1.86-.19-2.33-.36v.94c.5.2 1.58.39 2.33.39 2.38 0 3.14-1.2 3.14-2.41 0-1.28-.53-2.03-2.75-2.23h-.03zm8.58-2.47v-.86h-2.42v-2.5l-1.08.31v2.11l-1.56.44v.48h1.56v5c0 1.53 1.19 2.13 2.5 2.13.19 0 .52-.02.69-.05v-.89c-.19.03-.41.03-.61.03-.97 0-1.5-.39-1.5-1.34V6.94h2.42v.02-.01z"></path>
</svg>
</a>
  </div>

    <div class="Header-item f4 mr-0" role="navigation">
      <a class="HeaderMenu-link no-underline mr-3" data-ga-click="Header, sign in" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f7b345f75fbc3f75d48e49b9c24023a79cd4a63dc63c42874f7b729e346b2282" href="https://gist.github.com/auth/github?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">
        Sign&nbsp;in
</a>        <a class="HeaderMenu-link d-inline-block no-underline border color-border-default rounded px-2 py-1" data-ga-click="Header, sign up" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="78cc466708732ab6b09c4650ed14a2f2767b5b61546c1253b092de4b8fd8d380" href="/join?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f&amp;source=header-gist">
          Sign&nbsp;up
</a>    </div>
</div>

    </div>

  <div id="start-of-content" class="show-on-focus"></div>

    <div data-pjax-replace id="js-flash-container" data-turbo-replace>

  <template class="js-flash-template">
    <div class="flash flash-full  {{ className }}">
  <div class="px-2" >
    <button class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
      <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path>
</svg>
    </button>

      <div>{{ message }}</div>

  </div>
</div>
  </template>
</div>


  <include-fragment class="js-notification-shelf-include-fragment" data-base-src="https://github.com/notifications/beta/shelf"></include-fragment>

  <div
    class="application-main "
    data-commit-hovercards-enabled
    data-discussion-hovercards-enabled
    data-issue-and-pr-hovercards-enabled
  >
        <div itemscope itemtype="http://schema.org/Code">
    <main id="gist-pjax-container" data-pjax-container>


  <div class="gist-detail-intro gist-banner pb-3">
    <div class="text-center container-lg px-3">
      <p class="lead">
        Instantly share code, notes, and snippets.
      </p>
    </div>
  </div>

<div class="gisthead pagehead pb-0 pt-3 mb-4">
  <div class="px-0">



<div class="mb-3 d-flex px-3 px-md-3 px-lg-5">
  <div class="flex-auto min-width-0 width-fit mr-3">
    <div class="d-flex">
      <div class="d-none d-md-block">
        <a class="mr-2 flex-shrink-0" data-hovercard-type="user" data-hovercard-url="/users/bdfinst/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/bdfinst"><img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/8195127?s=64&amp;v=4" width="32" height="32" alt="@bdfinst" /></a>
      </div>
      <div class="d-flex flex-column width-full">
          <div class="d-flex flex-row width-full">
            <h1 class="wb-break-word f3 text-normal mb-md-0 mb-1">
              <span class="author"><a data-hovercard-type="user" data-hovercard-url="/users/bdfinst/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/bdfinst">bdfinst</a></span><!--
                  --><span class="mx-1 color-fg-muted">/</span><!--
                  --><strong itemprop="name" class="css-truncate-target mr-1" style="max-width: 410px"><a href="/bdfinst/51e281dd7ad96f2c2590359ad770541f">addWholeNumbers.js</a></strong>
            </h1>

        </div>

        <div class="note m-0">
          Created <time-ago datetime="2021-01-16T14:12:39Z" data-view-component="true" class="no-wrap">Jan 16, 2021</time-ago>
        </div>
      </div>
    </div>
  </div>

  <ul class="d-md-flex d-none pagehead-actions float-none">

      <li>
          <a class="btn btn-sm btn-with-count tooltipped tooltipped-n" aria-label="You must be signed in to star a gist" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist star button&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="36237ef14caa5b7ffa0d040bcc142af41a2b014f1cc9ee00ee5f7930f96e8187" href="/login?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star">
    <path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path>
</svg>
    Star
</a>
    <a class="social-count" aria-label="0 users starred this gist" href="/bdfinst/51e281dd7ad96f2c2590359ad770541f/stargazers">
      0
</a>
      </li>

        <li>
            <a class="btn btn-sm btn-with-count tooltipped tooltipped-n" aria-label="You must be signed in to fork a gist" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist fork button&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="a76eb0451de5b3da159b02ceffa12340bd697b98026101b07e2d13457034c9f5" href="/login?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked">
    <path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
</svg>
    Fork
</a>    <span class="social-count">0</span>

        </li>
  </ul>
</div>

  <div class="d-block d-md-none px-3 px-md-3 px-lg-5 mb-3">
      <a class="btn btn-sm btn-block tooltipped tooltipped-n" aria-label="You must be signed in to star a gist" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;gist star button&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="36237ef14caa5b7ffa0d040bcc142af41a2b014f1cc9ee00ee5f7930f96e8187" href="/login?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star">
    <path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path>
</svg>
    Star
</a>

  </div>

<div class="d-flex flex-md-row flex-column px-0 pr-md-3 px-lg-5">
  <div class="flex-md-order-1 flex-order-2 flex-auto">
    <nav class="UnderlineNav box-shadow-none px-3 px-lg-0 "
     aria-label="Gist"
     data-pjax="#gist-pjax-container">

  <div class="UnderlineNav-body">
    <a class="js-selected-navigation-item selected UnderlineNav-item" data-pjax="true" data-hotkey="g c" aria-current="page" data-selected-links="gist_code /bdfinst/51e281dd7ad96f2c2590359ad770541f" href="/bdfinst/51e281dd7ad96f2c2590359ad770541f">
      <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code UnderlineNav-octicon">
    <path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path>
</svg>
      Code
</a>
      <a class="js-selected-navigation-item UnderlineNav-item" data-pjax="true" data-hotkey="g r" data-selected-links="gist_revisions /bdfinst/51e281dd7ad96f2c2590359ad770541f/revisions" href="/bdfinst/51e281dd7ad96f2c2590359ad770541f/revisions">
        <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-commit">
    <path fill-rule="evenodd" d="M10.5 7.75a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm1.43.75a4.002 4.002 0 01-7.86 0H.75a.75.75 0 110-1.5h3.32a4.001 4.001 0 017.86 0h3.32a.75.75 0 110 1.5h-3.32z"></path>
</svg>
        Revisions
        <span title="1" data-view-component="true" class="Counter hx_reponav_item_counter">1</span>
</a>

  </div>
</nav>

  </div>

  <div class="d-md-flex d-none flex-items-center flex-md-order-2 flex-order-1 file-navigation-options" data-multiple>

    <div class="d-lg-table d-none">
      <div class="file-navigation-option v-align-middle">

  <div class="d-md-flex d-none">
    <div class="input-group">
      <div class="input-group-button">
        <details class="details-reset details-overlay select-menu">
          <summary data-ga-click="Repository, clone Embed, location:repo overview" data-view-component="true" class="select-menu-button btn-sm btn">  <span data-menu-button>Embed</span>

</summary>          <details-menu
            class="select-menu-modal position-absolute"
            data-menu-input="gist-share-url"
            style="z-index: 99;" aria-label="Clone options">
            <div class="select-menu-header">
              <span class="select-menu-title">What would you like to do?</span>
            </div>
            <div class="select-menu-list">
                <button name="button" type="button" class="select-menu-item width-full" aria-checked="true" role="menuitemradio" value="&lt;script src=&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f.js&quot;&gt;&lt;/script&gt;" data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;EMBED&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="d4de374b2ae38e0fd1f9f5ca0ec51c79bbc57c545a881c25ee09f35c52e302a5">
                  <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check select-menu-item-icon">
    <path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
</svg>
                  <div class="select-menu-item-text">
                    <span class="select-menu-item-heading" data-menu-button-text>

                      Embed
                    </span>
                      <span class="description">
                        Embed this gist in your website.
                      </span>
                  </div>
</button>                <button name="button" type="button" class="select-menu-item width-full" aria-checked="false" role="menuitemradio" value="https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f" data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;SHARE&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8370db36b63d1192dd7e7ead77a3ab17ccccd36973255ad0f51a105372300719">
                  <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check select-menu-item-icon">
    <path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
</svg>
                  <div class="select-menu-item-text">
                    <span class="select-menu-item-heading" data-menu-button-text>

                      Share
                    </span>
                      <span class="description">
                        Copy sharable link for this gist.
                      </span>
                  </div>
</button>                <button name="button" type="button" class="select-menu-item width-full" aria-checked="false" role="menuitemradio" value="https://gist.github.com/51e281dd7ad96f2c2590359ad770541f.git" data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;USE_HTTPS&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="bb5cbd72d0536677b1ffb7abd5ef971a501ae0e3d043cdc841bd2ce6961e9356">
                  <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check select-menu-item-icon">
    <path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
</svg>
                  <div class="select-menu-item-text">
                    <span class="select-menu-item-heading" data-menu-button-text>
                      Clone via
                      HTTPS
                    </span>
                      <span class="description">
                        Clone with Git or checkout with SVN using the repository's web address.
                      </span>
                  </div>
</button>            </div>
            <div class="select-menu-list">
              <a role="link" class="select-menu-item select-menu-action" href="https://docs.github.com/articles/which-remote-url-should-i-use" target="_blank">
                <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-question select-menu-item-icon">
    <path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zM6.92 6.085c.081-.16.19-.299.34-.398.145-.097.371-.187.74-.187.28 0 .553.087.738.225A.613.613 0 019 6.25c0 .177-.04.264-.077.318a.956.956 0 01-.277.245c-.076.051-.158.1-.258.161l-.007.004a7.728 7.728 0 00-.313.195 2.416 2.416 0 00-.692.661.75.75 0 001.248.832.956.956 0 01.276-.245 6.3 6.3 0 01.26-.16l.006-.004c.093-.057.204-.123.313-.195.222-.149.487-.355.692-.662.214-.32.329-.702.329-1.15 0-.76-.36-1.348-.863-1.725A2.76 2.76 0 008 4c-.631 0-1.155.16-1.572.438-.413.276-.68.638-.849.977a.75.75 0 101.342.67z"></path>
</svg>
                <div class="select-menu-item-text">
                  Learn more about clone URLs
                </div>
              </a>
            </div>
          </details-menu>
        </details>
      </div>

      <input
        id="gist-share-url"
        type="text"
        data-autoselect
        class="form-control input-monospace input-sm"
        value="&lt;script src=&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f.js&quot;&gt;&lt;/script&gt;"
        aria-label="Clone this repository at &lt;script src=&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f.js&quot;&gt;&lt;/script&gt;"
        readonly>

      <div class="input-group-button">
        <clipboard-copy for="gist-share-url" aria-label="Copy to clipboard" class="btn btn-sm zeroclipboard-button" data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;COPY_URL&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="1ece61d9bc63f783e60d58ab523362803599fde57bbb515665ebfb2627f78e45"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy">
    <path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path>
</svg></clipboard-copy>
      </div>
    </div>
  </div>
</div>

    </div>

    <div class="ml-2 file-navigation-option">
    <a class="btn btn-sm tooltipped tooltipped-s tooltipped-multiline js-remove-unless-platform" data-platforms="windows,mac" aria-label="Save bdfinst/51e281dd7ad96f2c2590359ad770541f to your computer and use it in GitHub Desktop." data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;OPEN_IN_DESKTOP&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="662230b09481d5cc56c934b5f3057be12bbfc82d6f60ed2a5d8ede9dfacf47c9" href="https://desktop.github.com"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-desktop-download">
    <path d="M4.927 5.427l2.896 2.896a.25.25 0 00.354 0l2.896-2.896A.25.25 0 0010.896 5H8.75V.75a.75.75 0 10-1.5 0V5H5.104a.25.25 0 00-.177.427z"></path><path d="M1.573 2.573a.25.25 0 00-.073.177v7.5a.25.25 0 00.25.25h12.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-3a.75.75 0 110-1.5h3A1.75 1.75 0 0116 2.75v7.5A1.75 1.75 0 0114.25 12h-3.727c.099 1.041.52 1.872 1.292 2.757A.75.75 0 0111.25 16h-6.5a.75.75 0 01-.565-1.243c.772-.885 1.192-1.716 1.292-2.757H1.75A1.75 1.75 0 010 10.25v-7.5A1.75 1.75 0 011.75 1h3a.75.75 0 010 1.5h-3a.25.25 0 00-.177.073zM6.982 12a5.72 5.72 0 01-.765 2.5h3.566a5.72 5.72 0 01-.765-2.5H6.982z"></path>
</svg></a>
</div>

    <div class="ml-2">
      <a class="btn btn-sm" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;clone_or_download.click&quot;,&quot;payload&quot;:{&quot;feature_clicked&quot;:&quot;DOWNLOAD_ZIP&quot;,&quot;git_repository_type&quot;:&quot;GIST&quot;,&quot;gist_id&quot;:107415153,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="45dc0a4bee9878029bd0e991b52ba5590b40b4c29c7c9b400c83ba9b9d693407" data-ga-click="Gist, download zip, location:gist overview" href="/bdfinst/51e281dd7ad96f2c2590359ad770541f/archive/7962860f9ed312c0de521b3347fb4e5dddec8750.zip">Download ZIP</a>
    </div>
  </div>
</div>

  </div>
</div>

<div class="container-lg px-3">
  <div class="repository-content gist-content" >

  <div>
      <div itemprop="about">
    Adding two numbers badly
  </div>

        <div class="js-gist-file-update-container js-task-list-container file-box">
  <div id="file-addwholenumbers-js" class="file my-2">
      <div class="file-header d-flex flex-md-items-center flex-items-start">
        <div class="file-actions flex-order-2 pt-0">

          <a href="/bdfinst/51e281dd7ad96f2c2590359ad770541f/raw/7962860f9ed312c0de521b3347fb4e5dddec8750/addWholeNumbers.js" data-view-component="true" class="btn-sm btn">  Raw

</a>
        </div>
        <div class="file-info pr-4 d-flex flex-md-items-center flex-items-start flex-order-1 flex-auto">
          <span class="mr-1">
            <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code-square color-fg-muted">
    <path fill-rule="evenodd" d="M1.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V1.75a.25.25 0 00-.25-.25H1.75zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm9.22 3.72a.75.75 0 000 1.06L10.69 8 9.22 9.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM6.78 6.53a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 101.06-1.06L5.31 8l1.47-1.47z"></path>
</svg>
          </span>
          <a class="wb-break-all" href="#file-addwholenumbers-js">
            <strong class="user-select-contain gist-blob-name css-truncate-target">
              addWholeNumbers.js
            </strong>
          </a>
        </div>
      </div>

    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-javascript  gist-border-0">


<div class="js-check-bidi js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>

    <span>
      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>

  <div data-view-component="true" class="flash-action">      <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">  Show hidden characters

</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>
</span></template>

  <table class="highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file" data-tab-size="8" data-paste-markdown-skip data-tagsearch-lang="JavaScript" data-tagsearch-path="addWholeNumbers.js">
        <tr>
          <td id="file-addwholenumbers-js-L1" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-addwholenumbers-js-LC1" class="blob-code blob-code-inner js-file-line"><span class=pl-k>function</span> <span class=pl-en>addWholeNumbers</span><span class=pl-kos>(</span><span class=pl-s1>a</span><span class=pl-kos>,</span> <span class=pl-s1>b</span><span class=pl-kos>)</span> <span class=pl-kos>{</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L2" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-addwholenumbers-js-LC2" class="blob-code blob-code-inner js-file-line">  <span class=pl-k>if</span> <span class=pl-kos>(</span><span class=pl-s1>a</span> <span class=pl-c1>%</span> <span class=pl-c1>1</span> <span class=pl-c1>===</span> <span class=pl-c1>0</span> <span class=pl-c1>&amp;&amp;</span> <span class=pl-s1>b</span> <span class=pl-c1>%</span> <span class=pl-c1>1</span> <span class=pl-c1>===</span> <span class=pl-c1>0</span><span class=pl-kos>)</span> <span class=pl-kos>{</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L3" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-addwholenumbers-js-LC3" class="blob-code blob-code-inner js-file-line">    <span class=pl-k>return</span> <span class=pl-s1>a</span> <span class=pl-c1>+</span> <span class=pl-s1>b</span><span class=pl-kos>;</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L4" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-addwholenumbers-js-LC4" class="blob-code blob-code-inner js-file-line">  <span class=pl-kos>}</span> <span class=pl-k>else</span> <span class=pl-kos>{</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L5" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-addwholenumbers-js-LC5" class="blob-code blob-code-inner js-file-line">    <span class=pl-k>return</span> <span class=pl-v>NaN</span><span class=pl-kos>;</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L6" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-addwholenumbers-js-LC6" class="blob-code blob-code-inner js-file-line">  <span class=pl-kos>}</span></td>
        </tr>
        <tr>
          <td id="file-addwholenumbers-js-L7" class="blob-num js-line-number js-code-nav-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-addwholenumbers-js-LC7" class="blob-code blob-code-inner js-file-line"><span class=pl-kos>}</span></td>
        </tr>
  </table>
</div>

    </div>

  </div>
</div>

      <a name="comments"></a>
      <div class="js-quote-selection-container" data-quote-markdown=".js-comment-body">
        <div class="js-discussion "
        >
          <div class="ml-md-6 pl-md-3 ml-0 pl-0">


<!-- Rendered timeline since 2021-01-16 06:12:39 -->
<div id="partial-timeline-marker"
      class="js-timeline-marker js-updatable-content"
      data-last-modified="Sat, 16 Jan 2021 14:12:39 GMT"
      >
</div>

          </div>

          <div class="discussion-timeline-actions">
              <div data-view-component="true" class="flash flash-warn mt-3">


    <a rel="nofollow" class="btn btn-primary" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;signed out comment&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f0bb5a1fdd3c27d5886fb3315841d197252cb9d6dc179295f83ee8ec3425b386" href="/join?source=comment-gist">Sign up for free</a>
    <strong>to join this conversation on GitHub</strong>.
    Already have an account?
    <a rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;signed out comment&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://gist.github.com/bdfinst/51e281dd7ad96f2c2590359ad770541f&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="93a90ab89f91ac9057cf3e95eacf2e0363313ef0bac23ecbf4026158355be804" data-test-selector="comments-sign-in-link" href="/login?return_to=https%3A%2F%2Fgist.github.com%2Fbdfinst%2F51e281dd7ad96f2c2590359ad770541f">Sign in to comment</a>


</div>
          </div>
        </div>
      </div>
</div>
  </div>
</div><!-- /.container -->

    </main>
  </div>

  </div>

          <footer class="footer width-full container-lg p-responsive" role="contentinfo">

  <div class="position-relative d-flex flex-items-center pb-2 f6 color-fg-muted border-top color-border-muted flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap mt-6 pt-6">
    <ul class="list-style-none d-flex flex-wrap col-0 col-lg-2 flex-justify-start flex-lg-justify-between mb-2 mb-lg-0">
      <li class="mt-2 mt-lg-0 d-flex flex-items-center">
        <a aria-label="Homepage" title="GitHub" class="footer-octicon mr-2" href="https://github.com">
          <svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github">
    <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
</a>        <span>
        &copy; 2022 GitHub, Inc.
        </span>
      </li>
    </ul>
    <ul class="list-style-none d-flex flex-wrap col-12 col-lg-8 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0">
        <li class="mr-3 mr-lg-0"><a href="https://docs.github.com/en/github/site-policy/github-terms-of-service" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to terms&quot;,&quot;label&quot;:&quot;text:terms&quot;}">Terms</a></li>
        <li class="mr-3 mr-lg-0"><a href="https://docs.github.com/en/github/site-policy/github-privacy-statement" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to privacy&quot;,&quot;label&quot;:&quot;text:privacy&quot;}">Privacy</a></li>
        <li class="mr-3 mr-lg-0"><a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to security&quot;,&quot;label&quot;:&quot;text:security&quot;}" href="https://github.com/security">Security</a></li>
        <li class="mr-3 mr-lg-0"><a href="https://www.githubstatus.com/" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to status&quot;,&quot;label&quot;:&quot;text:status&quot;}">Status</a></li>
        <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to help, text:Docs" href="https://docs.github.com">Docs</a></li>
        <li class="mr-3 mr-lg-0"><a href="https://support.github.com?tags=dotcom-footer" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to contact&quot;,&quot;label&quot;:&quot;text:contact&quot;}">Contact GitHub</a></li>
        <li class="mr-3 mr-lg-0"><a href="https://github.com/pricing" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to Pricing&quot;,&quot;label&quot;:&quot;text:Pricing&quot;}">Pricing</a></li>
      <li class="mr-3 mr-lg-0"><a href="https://docs.github.com" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to api&quot;,&quot;label&quot;:&quot;text:api&quot;}">API</a></li>
      <li class="mr-3 mr-lg-0"><a href="https://services.github.com" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to training&quot;,&quot;label&quot;:&quot;text:training&quot;}">Training</a></li>
        <li class="mr-3 mr-lg-0"><a href="https://github.blog" data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to blog&quot;,&quot;label&quot;:&quot;text:blog&quot;}">Blog</a></li>
        <li><a data-ga-click="Footer, go to about, text:about" href="https://github.com/about">About</a></li>
    </ul>
  </div>
  <div class="d-flex flex-justify-center pb-6">
    <span class="f6 color-fg-muted"></span>
  </div>
</footer>

  <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden>
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>
    <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
      <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path>
</svg>
    </button>
    You can't perform that action at this time.
  </div>

  <div class="js-stale-session-flash flash flash-warn flash-banner" hidden
    >
    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>
    <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span>
    <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span>
  </div>
    <template id="site-details-dialog">
  <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open>
    <summary role="button" aria-label="Close dialog"></summary>
    <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
      <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
        <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path>
</svg>
      </button>
      <div class="octocat-spinner my-6 js-details-dialog-spinner"></div>
    </details-dialog>
  </details>
</template>

    <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;" tabindex="0">
  <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;">
  </div>
</div>

    <template id="snippet-clipboard-copy-button">
  <div class="zeroclipboard-container position-absolute right-0 top-0">
    <clipboard-copy aria-label="Copy" class="ClipboardButton btn js-clipboard-copy m-2 p-0 tooltipped-no-delay" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2">
    <path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path>
</svg>
      <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2">
    <path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>

  </body>
</html>

So I have a function that will check both inputs to verify that they are evenly divisible by 1. If they are not, then it returns NaN. Since alpha characters are also not divisible by 1, those will be trapped too. Naturally, we have tests for this because I am also told I need over 90% test coverage. Let’s run the coverage report!

code-coverage-ex (main ✔) » npm run coverage

Adding two whole numbers
    ✓ should return the sum of two whole numbers
    ✓ should return NaN if one number is not a whole number

2 passing (5ms)

--------------------|---------|----------|---------|---------|
File                | % Stmts | % Branch | % Funcs | % Lines |
--------------------|---------|----------|---------|---------|
All files           |     100 |      100 |     100 |     100 |
 addWholeNumbers.js |     100 |      100 |     100 |     100 |
--------------------|---------|----------|---------|---------|

There we go, 100% coverage for every statement, code branch, and function. The gold standard of quality.

I even have a nice report for the product owner and my manager.

I’m done and ready to hand this off to whoever will be doing support for me. I’m a developer, after all, support is someone else’s problem.

Later that month, my change is delivered to production and customers start using my 100% covered code to add products to their shopping carts. Random quantity issues begin occurring and customers are getting angry about overcharges and massive over-shipment of products. Weird. We have really high code coverage. What went wrong?

Let’s log the output from the function

2 + 2 = 4
2.1 + 2 = NaN
A + 2 = NaN
2 + 2 = 22

So, the first three runs look great. What happened to the last run? Let’s look at the code.

console.log(`2 + 2 = ${addWholeNumbers(2, 2)}`)
console.log(`2.1 + 2 = ${addWholeNumbers(2.1, 2)}`)
console.log(`A + 2 = ${addWholeNumbers("A", 2)}`)
console.log(`2 + 2 = ${addWholeNumbers("2", 2)}`)

Hang on. That last run should have returned NaN too. We tested for strings

const expect = require("chai").expect
const addWholeNumbers = require("../addWholeNumbers")

describe("Adding two whole numbers", () => {
  it("should return the sum of two whole numbers", () => {
    expect(addWholeNumbers(2, 2)).to.be.not.NaN
  })
  it("should return NaN if one number is not a whole number", () => {
    expect(addWholeNumbers(1.1, 2)).to.be.NaN
    expect(addWholeNumbers("A", 2)).to.be.NaN
  })
})

The tests above test very little that is meaningful to ensure the function works correctly. It was written to meet the goal of high code coverage. The customers didn’t care about that goal. The goal they cared about was that the function worked as intended. JavaScript is a very friendly language for rapid development. It typecasts for you. 2 + 2 = 4 but the + operator is also the string concatenation operator so "2" + 2 = "22". Also, numbers in a string can have number operators used on them so the modulus operator in the function to verify a whole number will typecast a numeric string as well. This is an easy mistake that could be caught in a review of the tests if the size of the code review is small enough. All we need to do is add a test for this problem.

The scenario above assumes the best intentions but nieve testing skills. We can fix this by focusing on the actual goal, “we are confident we are meeting the customer’s needs” and then teaching people how to test.

Let’s cover another scenario that I’ve personally witnessed as an outcome of “you must have 90% code coverage!”. I was code reviewing some old code one day. Reviewing tests is really the first priority anyone should have. I came across something terrifying. The code had very high coverage and was completely untested. Using the function above as an example, the tests were similar to this:

it('Should add two whole numbers' () => {
   addWholeNumbers(2, 2)
   addWholeNumbers(1.1, 0)
})

This test will also report 100% code coverage. It doesn’t actually run any tests. If you are judging a team on code coverage, how many of your tests look like this?

Testing is what professional software developers do. Pro’s have pride in their work and want to deliver working solutions to business problems. However, everyone will adapt to the environment they are placed in or they will choose to move to another environment. If developers are pushed to meet dates and push out features as fast as possible, quality processes are squeezed out. If they are then told to improve code coverage, code coverage will go up. The only thing scarier than untested code is code tested with tests you don’t trust.

What is the value of code coverage reporting? To find untested code that should be tested. There should never be a “code converge goal”. Instead, we should focus on what the customer needs and use an actual quality process: reduce the size of changes, focus on improving the quality signal from the pipeline, and deliver changes rapidly to find out if our fully tested application is tested using bad assumptions.

Only our users can define quality. A test is only our prediction of what they think. Deliver so you can test the test.


Written on January 16, 2021 by Bryan Finster.

Originally published on Medium

Measuring to Fail

Are you leading teams and want to make sure they are productive? There are many ways to do that. Many are doing it incorrectly.

It seems like I’m always talking about metrics. When I was first exploring how to grow continuous delivery as a team capability, I needed to find ways to find out we were doing it correctly. I mostly did it wrong at the start, but I sought out better information and then tried to verify if it was better.

Continuous delivery is not using Jenkins to automate build and deploy and hoping things don’t break. CD is the process of optimizing the flow to delivery, from request to the end-user, to improve quality feedback, continuously improve security and quality gates, and get user feedback a rapidly as possible. To know if you’re doing this well you need to understand metrics. However, even if you aren’t focusing on CD you need to understand how to measure because you cannot improve what you cannot measure.

Measuring Productivity

A common question is, “how productive are we?”. There are many ways to measure productivity, so let’s start with the bad ones.

Utilization

I had a conversation recently with a friend who told me their manager wanted to measure the utilization of the team to make sure they were fully utilized. In fact, the manager’s opinion was that if the teams weren’t sometimes failing to meet their commitments, they probably weren’t working hard enough. This attitude is something I hear frequently from many companies. I even hear it from manufacturing companies. That’s just astounding to me. No one in manufacturing would want 100% utilization of the assembly line. However, let’s ponder this in terms most engineering managers should understand.

This is a highway

Highway

This is a fully utilized highway

Traffic jam

If you had a high-value delivery to get to your customer, which would you prefer to use?

Committed vs Completed

We want to make sure that teams are “meeting their commitments”. They promised they could deliver and we need to hold them accountable, right? So let’s focus on how much they failed to deliver. In the past five sprints, the team committed to 100 story points and delivered, on average, 50 story points. They really must not be very productive, right? So we will use this ratio to make them do better. Look! it worked! It just 1 sprint that lazy team decided to meet their commitments! they doubled their velocity! How’d they do that? We need all teams doing that?

They could reduce their commitment and inflate the story points or they could skip quality processes. We definitely want to encourage that because the metrics look better. Now everyone is happy but the customer.

Doing it better

Productivity is our ability to deliver what the customer needs and to continuously improve our ability to be more efficient and more effective at doing that in a sustainable way that reduces neither the health of the code nor the health of the team. We need to measure the things the customer cares about so we can make them happy. If the customer is unhappy, it really doesn’t matter how management feels about the metrics they are using. What does the customer care about? They want to reliably receive quality work when they need it. We can measure delivery frequency, change fail rates, bug reports, customer feedback, and team feedback. Productivity is not about how many features we can deliver. It can be more productive to deliver less because we are delivering the right features instead of racing to produce things that no one wants. Remember, activity is not work. If you add energy to an inefficient system, you mostly generate heat, not work. That is only valuable if the customer is cold.


Written on January 14, 2021 by Bryan Finster.

Originally published on Medium