<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dvnc0</title>
    <description>The latest articles on DEV Community by dvnc0 (@dvnc0).</description>
    <link>https://dev.to/dvnc0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1053756%2F434ec446-e92c-48dc-a47e-07c4e725196c.jpeg</url>
      <title>DEV Community: dvnc0</title>
      <link>https://dev.to/dvnc0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dvnc0"/>
    <language>en</language>
    <item>
      <title>It's Ok to Let Go</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Sun, 05 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dvnc0/its-ok-to-let-go-54np</link>
      <guid>https://dev.to/dvnc0/its-ok-to-let-go-54np</guid>
      <description>&lt;h2&gt;
  
  
  What are we holding on to?
&lt;/h2&gt;

&lt;p&gt;Recently, I read through an article on the Every blog, &lt;a href="https://every.to/source-code/i-stopped-writing-code-my-productivity-exploded" rel="noopener noreferrer"&gt;"I Stopped Writing Code. My Productivity Exploded"&lt;/a&gt; by Yash Poojary. I read it a few times. The article details the addition of a new feature to an application without requiring a single line of code. Poojary essentially gives control to Claude Code and discusses what he wants, how it should work, and pastes errors in, without writing a single line of code. Immediately, I was hooked when I read "In the meantime, I had begun to question my value as a developer. I used to solve coding problems like sudoku puzzles; now I was watching Claude solve them while I just supervised. The brain rot was real, and I was terrified." A lot of developers enjoy the puzzle - the part of you that silently screams and celebrates every time you solve something complex or reach a root cause. It's the puzzle, the chase, whatever you call it. As our field evolves, there is a quiet fear that we will lose that, that the puzzle will vanish, and we'll no longer have our silent victories. Poojary goes on to discuss how he focused on design and user experience, letting the LLM handle the coding. Eventually, you get to this quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The fear of brain rot had haunted me for months. And it was justified: I couldn't write a hashing algorithm from scratch like before. When errors popped up, I'd paste them into Claude like a helpless child. The knowledge I'd spent years accumulating was evaporating. But after I made this new Sparkle feature, I realized that I no longer missed it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Poojary talks about transforming from "someone who writes code to someone who ships solutions." By the end of the article, I was left wondering: what are we really holding on to? I have been thinking about this for about a week now, going over it in my head and writing down thoughts about this article in one of my commonplace books. I think it comes down to wanting to be in control and feeling the rush of solving the puzzle, admiring our own work, and reminding ourselves that we still have &lt;em&gt;it&lt;/em&gt;. This hesitance to let go is slowing us down and will leave some of us behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The emerald
&lt;/h2&gt;

&lt;p&gt;I'm guilty of this as well, even as an early adopter of using LLMs to code and someone who dove in headfirst. I still gravitated towards Cursor and handling the complex code by myself, never entirely giving in. I didn't like Claude Code and felt more at home with Cursor, with the editor begging me to write a few lines of code. I was convinced the LLM needed me to do the complex parts. I even went as far as logging out of GitHub on VSCode so I could have an AI-free environment to work in for the times when I really didn't need the distraction, because &lt;em&gt;I&lt;/em&gt; needed to solve the complex problem, &lt;em&gt;I&lt;/em&gt; needed to solve the puzzle. One of the most challenging things in life is admitting when you need to change. Admitting to yourself that you need to grow and evolve. Patterns are easy to get stuck in; breaking them is hard. You need to develop and shift your skill set to grow with the industry. I'm not saying abandon all hope and give up writing code. Instead, it's ok to let go, to forget syntax, to forget the years of documentation, and to forget which arguments come first. Focus on system design, architecture, user experience, being human, and understanding what humans like and dislike.&lt;/p&gt;

&lt;p&gt;It's not easy for some of us to realize that what we have spent decades mastering, we can now let go of and forget. That after decades of being the computer, we no longer have to be. That the things we struggled to learn and grasp and remember years later no longer matter. It is hard to let go; it is hard to feel like that time and dedication are no longer a necessity, but they aren't. There is a Marcus Aurelius quote that I've been thinking about, which discusses the need for praise and admiration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is an emerald suddenly flawed if no one admires it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I realized that a lot of what I was holding on to was the puzzle and self-praise, the "I still got it" moment, when I was solving a complex problem. If I no longer have that moment, if I give up control, does that make me a less effective developer? If you focus on architecture, systems design, and UI/UX instead of syntax and complex algorithms, are you less of a developer than you were five years ago?&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning to evolve
&lt;/h2&gt;

&lt;p&gt;I tried a similar experiment. I was looking for a simple image resizing tool with basic settings and a few presets for a site of mine that is image-heavy. I decided to install Claude Code again and talk. The experience was enlightening. I was able to focus on the solution and outcome, keeping that project-level thinking in place, and didn't write a single line of code. At the end of the session, I had the application I needed, with preset sizes I had defined and the basic features I required, without a single line of code and without opening an IDE (unless you count using vim to review the code). The puzzle was still solved, and instead of writing the code and struggling with new libraries, I was the conductor, the manager, the project-level thinker, describing the solution.&lt;/p&gt;

&lt;p&gt;This will likely be the most significant hurdle for AI to overcome with senior developers. I know many seniors who use AI, but still feel that the LLM needs them to handle the complex parts. In some cases, this may be true, but what comes next? What about after LLMs, when AI has actual memory and learns and evolves faster than we ever could imagine? What then, will the AI still need you to handle the complex parts?&lt;/p&gt;

&lt;p&gt;Borrowing from Marcus Aurelius again, time is like a river; the past is already long gone. If you're still holding on to what is long behind you, you're missing out on the future. Learn to be the conductor, evolve, grow, and let some of the decades of knowledge be replaced with new skills.&lt;/p&gt;

&lt;p&gt;It's ok to let go.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>ai</category>
      <category>growth</category>
    </item>
    <item>
      <title>Never Ending Work Day</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dvnc0/never-ending-work-day-3nll</link>
      <guid>https://dev.to/dvnc0/never-ending-work-day-3nll</guid>
      <description>&lt;h2&gt;
  
  
  I stop when the work is done
&lt;/h2&gt;

&lt;p&gt;Recently, I joined a call, and two junior developers were discussing their work and availability. I wasn't paying close attention at first, as I was wrapping up notes from the previous call. Then one of the developers asked what time the other leaves for the day, and the response was one you never want to hear: "I don't know, I just stop when I think the work is done." They went on to explain that the time sometimes is 8 PM, sometimes 5 PM, but it fluctuates. That immediately stopped me from scribbling down notes, and I started listening. The never-ending workday is a terrible way to work as a developer, I know, because I was that guy. I would zone out and code, working all kinds of hours because I felt like I "wasn't in a good place to stop."&lt;/p&gt;

&lt;p&gt;This is how you burn out. This is how you lose interest in projects. This is how you end up hating your job. Working 60 hours a week doesn't make you a hero; it doesn't make you better than everyone else; it just makes you tired. This mentality is particularly evident among junior developers who are striving to advance in their careers. There is some misguided belief that working those hours means you care more or you're willing to sacrifice for the organization. Really, it means you're working at a rate you can't sustain and are just doing more work than everyone else for no reason. Eventually, you burn out and productivity drops. Camille Fournier highlights these ideas in The Manager's Path, explaining that burnout happens when you are doing more than is sustainable. You can’t just keep going forever on adrenaline. Sustainable productivity matters more than heroics.&lt;/p&gt;

&lt;h2&gt;
  
  
  But then what do I do?
&lt;/h2&gt;

&lt;p&gt;Mentoring junior developers at this stage is crucial for senior developers and engineering managers. They need to identify the signs that someone is working in this manner and start working with them. They need to get them to a place where they understand that working at a sustainable rate is more important than working towards burnout. When developers are stuck in this hyper-work mentality, they don't see their efficiency dropping; they don't realize their maximum cognitive load is a bit lower, and the way they start thinking about features is getting sloppier. When you're burning the candle at both ends, no one wins.&lt;/p&gt;

&lt;p&gt;Highly effective teams are not effective because they are all working 60-hour workweeks. They are efficient, have optimized workflows, incredible teamwork and communication, and know when to stop. It is challenging to find an effective, highly productive team that works more than 40 hours a week; it simply is not sustainable. To get there, you need to counter the mindset that these borderline senior developers get, that they need to be the rock stars who work all night hammering out code to get a few more points in. Firstly, you need to talk to them. They need to hear it from you that you do not expect them to work more than 40 hours, that you do not expect them to be here all night, and that you are not judging their worth based on how many hours they can work or how close to burnout they can get. They need to hear and understand that you &lt;em&gt;want&lt;/em&gt; them to work less.&lt;/p&gt;

&lt;p&gt;They also need tools to help combat this mindset. This is where frameworks and mental models come in, which can start to help them break this habit. Make sure to set aside time to teach soft skills and incorporate some of these techniques to equip them with the tools they need to succeed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hemingway Bridge or Shut Down Ritual
&lt;/h3&gt;

&lt;p&gt;Hemingway talked about the importance of leaving yourself a bridge to the next session, a logical starting point. The importance of not stopping on a cliff but at a logical point where you know your next move. This applies to development too; you should stop at a point that is easy to define, and it should be easy to see where you can start again the next day. This should be relative to the end of a typical workday, not working until 8 PM. If they need to stop at 5:25 PM because they are in a logical spot to take notes on where they left off, they should. Let them pick it back up in the morning at a well-defined starting point. This is also part of developing a Shut Down Ritual. You should encourage them to create a habit of doing the same thing at the end of the day. Reviewing tasks, taking notes on where you left off, and jotting down todos for unfinished work. These all develop a ritual that helps create closure and removes these items from their brain. Your brain is not well-suited for holding onto information like this, which is why you will replay your todo list until the late hours of the night. Creating this ritual helps to shut down after work and leave at a healthy time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brain Dump
&lt;/h3&gt;

&lt;p&gt;Brain dumping is really similar to the Shut Down Ritual, and could be part of it. I break it out because it is also helpful to focus on when context switching throughout the day. This is transferring the context stored in your brain to a more permanent storage. Dumping the context, notes, where you left off, and any other relevant information that may help you when you return to that task, dump it into a text file or Markdown file somewhere. This is also a helpful pattern to use throughout the day when context switching between projects or meetings. It helps clear your mind to focus on the next task or shut down for the day, and provides a good starting point when you return. The most important takeaway is that it lets your brain rest. Your brain will naturally want to finish tasks and keep writing code, so stopping and clearing the state is a good way to take a break and let your brain rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeboxing
&lt;/h3&gt;

&lt;p&gt;Encourage developers to schedule dedicated coding time. This not only helps them dedicate time without context switches, but also creates a sustainable habit. It helps to get developers to stop before they are exhausted or before they code into the late hours of the night. Timeboxing also reinforces that progress comes from consistency and sustainability, rather than marathon efforts and heroics. At the same time, this supports the idea of a sustainable pace and reinforces that overworking is not the expectation and can cause a drop in velocity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parkinson's Law, Deliberate Practice, and Continuous Improvement
&lt;/h3&gt;

&lt;p&gt;Junior developers need to understand that work will expand to fill the time allocated to it. So when they say they will work until they feel it's done, they are inviting Parkinson's Law into the equation and letting it balloon their work time out of control. The more time you give up, the more work there will be to fill it. Once you get into this pattern, that balloon keeps growing until it pops. The idea of deliberate practice, or making small structured improvements, helps developers understand that it is not about hours, but about the value of their work. It teaches them to see things as compounding sessions that build to a bigger part of the whole, rather than focusing on reaching a finish line every day. This ties in with continuous improvement, stop at a healthy stopping point, and make improvements tomorrow. Small incremental changes are better than unsustainable heroics.&lt;/p&gt;

&lt;h2&gt;
  
  
  It takes time
&lt;/h2&gt;

&lt;p&gt;As leaders, we need to notice when developers are starting to fall into this pattern. We need to spend time mentoring and working with them to provide them with the tools and reassurance they need to break out of that habit. Reinforcing the expectation of 40 hours a week and providing them with tools and frameworks can enhance their quality of life, facilitate growth, and prevent burnout. The critical thing to remember is that it takes time; you need to spend time mentoring and coaching, as well as developing a relationship of trust with your team, and communicating openly and honestly. Without that base, you may never see the warning signs. Developers may not tell you they are working too much, or even worse, they may not believe you when you tell them they don't have to. So take the time.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>mentoring</category>
      <category>balance</category>
    </item>
    <item>
      <title>Stop Thinking About Bad Code</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dvnc0/stop-thinking-about-bad-code-492m</link>
      <guid>https://dev.to/dvnc0/stop-thinking-about-bad-code-492m</guid>
      <description>&lt;h2&gt;
  
  
  What is Good and what is bad?
&lt;/h2&gt;

&lt;p&gt;This is an argument as old as time. As long as humans have existed, we have tried to classify events, people, and actions as good or bad. Our perception can influence or sway how we act in relation to those events, people, or actions. This same thing happens with code; our perception of what is good or bad can change the outcome of how we approach code reviews, feature planning, or even just triaging a defect. We can become so obsessed with the idea of good or bad code that it clouds our judgment and distorts our actions. But what really is good or bad code? The definition is often fluid and changes with time, experience, tech stack, or maturity in the field. What is considered good code now can quickly decay into a "smell" in a few years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop thinking about code in absolutes
&lt;/h2&gt;

&lt;p&gt;We spend a lot of time defining what code should be. Design patterns, frameworks, style guides, manifestos on how to write code, and how many lines a function should have. I'm guilty of this trap too, thinking that some defined pattern is going to make my code better than it was before. I'm not ignoring the benefit of having a shared set of guidelines and standards when working with a larger group of developers. This is an essential part of keeping code maintainable, readable, and the cognitive load low, but is it a measure of good or bad code? It shouldn't be.&lt;/p&gt;

&lt;p&gt;Maintainable code, lacks complexity, and functions as intended has reached its designed purpose and completed its part in the ecosystem of whatever code you are working on now and in the future. Is that code good? To me, it may be, but to the avid believer of functional programming, it may be terrible, or to the Typescript purist, it may seem foolish and poorly structured. It is all subjective and mind-numbing, a distraction from what software should be, a solution that solves a problem in the least complex way.&lt;/p&gt;

&lt;p&gt;There is nothing inherently good or bad about code; it is a set of instructions designed to solve a problem. The idea of good or bad is a purely human construct, probably born with our obsession with labeling and classifying the things around us. This struggle is nothing new; from Greek and Roman philosophy (Marcus Aurelius wrote "progress for a rational mind means not accepting falsehood or uncertainty in its perception") to modern day, the battle of good and evil and the human drive to understand these ideas is easy to find. What does it actually accomplish when it comes to coding? Do you code review a developer that you think writes good code with less attention than one you feel writes bad code? Are you so blinded by your perception of a section of code that you can't see the simple fix right in front of you, and instead insist it has to be started anew? Often, the way we perceive something as good or bad will significantly change our actions, coding, and working with other developers is no different.&lt;/p&gt;

&lt;p&gt;What if you stop thinking about code as good or bad? Instead, think of it in good faith; this was, at the time, the best code a developer was able to write to solve a problem. Its story might be years of additions and decay, compromised by hasty bug fixes and out-of-scope changes. Again, it's not good or bad and is not some terrible thing someone did that should impact your actions. Instead, take it for what it is: someone's best effort at solving a problem. Don't let your perception of the code or the developer cloud your judgment and actions; that helps no one.&lt;/p&gt;

&lt;p&gt;Code is subject to entropy just like anything else; what was great code 10 years ago becomes bad practice and lessons learned. It's easy to forget that and label it as bad code with no hope, needing to be rebuilt from the ground up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be a mentor
&lt;/h2&gt;

&lt;p&gt;Once you escape the thinking that code is good or bad, it is easier to become an effective mentor, it is easier to be judgment-free, and focus on the things you can control or teach. When you realize that object oriented code or functional code both can solve the same problem just as well, or repeating code is not evil but actually lowers micro-context switches and cognitive load, and that maybe the developer just doesn't realize there is a more efficient or performant way to solve a problem but this is still their best effort and to them represents the sum of their knowledge and experience, you will start to change how you approach these interactions.&lt;/p&gt;

&lt;p&gt;When I think about imposter syndrome and how it impacts developers throughout their careers, it is buried in the idea that something is not good enough. Their code, their leadership, etc, imposter syndrome is centered around the idea that they are bad and everyone else is better, so they don't belong. It is a wild obsession we have with trying to fit things into a good or bad box that follows us throughout our entire careers. Instead, we should all just let go and take it for what it is, a solution to a problem. Being a better mentor and moving away from that idea of good or bad can significantly impact imposter syndrome and change the course of a developer's career. Understanding that something isn't good or bad but needs help to be more efficient or performant will change how you interact with your peers and with code. It will change how you look at legacy code from 15 years ago that you're stuck refactoring. Moving past the blinding answer of "it's bad and just needs to be burned down" will yield surprising results.&lt;/p&gt;

&lt;p&gt;Try it out, stop thinking about code as good or bad, and see what happens. It might surprise you.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>mentoring</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Keeping Mission Critical Code Running</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Wed, 06 Sep 2023 05:57:36 +0000</pubDate>
      <link>https://dev.to/dvnc0/mission-critical-code-safety-21l7</link>
      <guid>https://dev.to/dvnc0/mission-critical-code-safety-21l7</guid>
      <description>&lt;p&gt;After pushing a pretty nice little defect to production at work that broke arguably some of our most mission-critical code my mind went on its normal spiral of why and how can I avoid this in the future. It was not a spectacular defect it was pretty boring and fixed pretty quickly, but it got through to production and noisily broke critical code. As my mind raced around I remembered a few articles I had stumbled on at some point about NASA and the Jet Propulsion Laboratories rules for critical code. This is code meant to go to Mars and remain functional or fail as gracefully as possible so I figured it is probably a good area to dig around in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why NASA?
&lt;/h2&gt;

&lt;p&gt;The JPL Power of 10 rules are designed to limit specific C coding practices that make C code harder to peer review and statically analyze. In turn, these practices make C code more prone to errors and complex defects that could cause production failures on mission-critical code on other planets, imagine being that developer. NASA also focuses on defensive programming as a way to limit defects on systems running millions of miles away from Earth. I'm a PHP developer so these rules don't translate 100% to PHP, but they make you think about common patterns that can be eliminated to make critical code safer. I'm not going to talk about all the rules here but you can read them &lt;a href="https://en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Developing_Safety-Critical_Code" rel="noopener noreferrer"&gt;here&lt;/a&gt; or &lt;a href="https://yurichev.com/mirrors/C/JPL_Coding_Standard_C.pdf" rel="noopener noreferrer"&gt;in this PDF&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Relating these rules to PHP
&lt;/h3&gt;

&lt;p&gt;Like I said not all of these rules will directly translate to PHP, but the idea of eliminating coding practices that are prone to errors or make code hard to review does. What things can we do in PHP to make critical code safer?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use simple control flow logic and avoid nested loops and deeply nested conditionals. This makes code harder to follow, test, and statically analyze which increases the likelihood of defects. Mission-critical code should use as simple of control flow logic as possible.&lt;/li&gt;
&lt;li&gt;Always leave errors and warnings on when running your code and never ignore them.&lt;/li&gt;
&lt;li&gt;Wrap code in modular and reusable classes that can be tested and isolated, this also helps in restricting scope.&lt;/li&gt;
&lt;li&gt;Avoid global variables which can often introduce unintended side effects into your system&lt;/li&gt;
&lt;li&gt;Use types for all returns and arguments when possible. Clearly define interfaces and validate they are being fulfilled.&lt;/li&gt;
&lt;li&gt;Use static analysis to catch errors early&lt;/li&gt;
&lt;li&gt;Use graceful error handling to ensure mission-critical code continues to function even after errors&lt;/li&gt;
&lt;li&gt;Keep functions and classes as small and simple as possible&lt;/li&gt;
&lt;li&gt;Avoid references because they can introduce unintended behavior that is hard to catch and replicate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are loosely inspired by the Power of Ten Rules and the JPL guidelines. Going further down the rabbit hole we can take this to another level, reviewing &lt;a href="https://coder.today/tech/2017-11-09_nasa-coding-standards-defensive-programming-and-reliability-a-postmortem-static-analysis./" rel="noopener noreferrer"&gt;NASA coding standards, defensive programming, and reliability&lt;/a&gt; which provides summaries of some NASA defensive coding practices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As the article says test, test, and test. These should be unit tests and integration tests of mission-critical code. NASA operates on the idea that code must work flawlessly in production so testing is their best guarantee.&lt;/li&gt;
&lt;li&gt;Monitor production code for potential issues&lt;/li&gt;
&lt;li&gt;Treat warnings as errors, because eventually, they will be&lt;/li&gt;
&lt;li&gt;Use extreme caution when including code you do not control as part of your mission-critical code, it can change and it can introduce errors&lt;/li&gt;
&lt;li&gt;Treat your code as if it will be attacked and it will fail and defend against that&lt;/li&gt;
&lt;li&gt;Validate all input&lt;/li&gt;
&lt;li&gt;Ability to replicate production environment locally and enable fast development cycles&lt;/li&gt;
&lt;li&gt;Code reviews using both automated tools and peer review&lt;/li&gt;
&lt;li&gt;All warnings should be fixed before the code is sent to production&lt;/li&gt;
&lt;li&gt;100% unit test coverage&lt;/li&gt;
&lt;li&gt;Test for edge cases and all branches of a function&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Some additional items to think about
&lt;/h3&gt;

&lt;p&gt;These are things not mentioned in the NASA reading directly, but can really help when it comes to developers practicing defensive coding and peer code reviews.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure your code is well documented and that documentation is up to date&lt;/li&gt;
&lt;li&gt;Develop and follow coding standards and best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These are all things that we can do not only in PHP code but any code. Treating mission-critical code as code that &lt;em&gt;must&lt;/em&gt; run correctly every time it runs and taking a defensive stance to ensure that it does improve your product and the reliability of that product. My defect was the result of a mix of testing failures, good thing I could fix it and push it live and not have to wait for it to travel millions of miles. What rules do you follow to ensure your critical code stays functional?&lt;/p&gt;

</description>
      <category>php</category>
      <category>defensivecoding</category>
      <category>testing</category>
      <category>staticanalysis</category>
    </item>
    <item>
      <title>Recommendations For Writing Better Code.</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Sat, 10 Jun 2023 03:13:31 +0000</pubDate>
      <link>https://dev.to/dvnc0/recommendations-for-writing-better-code-464f</link>
      <guid>https://dev.to/dvnc0/recommendations-for-writing-better-code-464f</guid>
      <description>&lt;p&gt;Hopefully, the title caught your interest and more so the choice to use the word recommendations. I wanted to emphasize that these are recommendations, not rigid rules, for writing better code. The word "rules" implies that what I am writing has some authority to govern every software project created from this moment forward and all projects should adhere to this list, and that is just unrealistic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's back up
&lt;/h2&gt;

&lt;p&gt;Software projects vary greatly in their nature, and each project operates within its own unique context. Technology is constantly evolving, introducing new paradigms and approaches. It's because of this that these recommendations aim to provide guidance that can assist you in writing more effective code. The goal is to lower the cognitive load, improve code readability, and facilitate easier maintenance and comprehension. It's important to keep in mind that blindly applying a random set of suggestions to an existing legacy monolith during your first week on a project may hinder rather than help achieve these objectives. Don't say I didn't warn you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cognitive Load
&lt;/h3&gt;

&lt;p&gt;Cognitive load refers to the capacity of our brain's working memory and its ability to retain information or context without becoming overwhelmed. Remember the first time you encountered a codebase you had never worked in and attempting to debug an error. Navigating through the code, tracing its flow, and understanding its purpose requires you to hold various details in memory simultaneously. Cognitive load theory says eventually you will exhaust that working memory and not be able to keep all of that context in memory. I know I have felt that sudden confusion when trying to track through file after file to see where some function is included or digging to find what some random array of integers means. This is part of the reason I became such a huge note-taker when looking through new code. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Recommendations
&lt;/h2&gt;

&lt;p&gt;When reading these recommendations you will need to keep in mind the context of your projects and find a balance that makes sense for those projects. Again, what works in one project likely will not work in every project.&lt;/p&gt;

&lt;p&gt;These recommendations will generally try to achieve the following goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make your code easier to maintain and work in&lt;/li&gt;
&lt;li&gt;Improve the readability of your code&lt;/li&gt;
&lt;li&gt;Decrease the cognitive load required for new developers who inherit your code&lt;/li&gt;
&lt;li&gt;Make it easier to debug and troubleshoot your code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Functions should be concise.
&lt;/h3&gt;

&lt;p&gt;I can't sit here and tell you functions should be no more than four lines and that's it! That is not practical, it can be done, but it is not realistic. It adds way too much to the cognitive cost of your code which eventually makes your code harder to follow and work in. Instead, your functions should be concise units of code. A concise function presents information clearly and comprehensively, minimizing redundancy. This helps improve code readability and reduces the cognitive load required to understand the function's purpose and behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use meaningful, descriptive, and searchable variable and function names.
&lt;/h3&gt;

&lt;p&gt;Choose variable and function names that provide context and accurately describe their purpose. Meaningful and descriptive names help other developers understand what the code is doing without needing to dive into the details. Does this mean you always need to write &lt;code&gt;someSuperLongVariableName&lt;/code&gt;? No, keep in mind the context of your function or the unit of code you are working on, sometimes an abbreviation makes perfect sense in the context of the rest of the code. Giving functions and variables meaningful names helps with autocomplete as well, a developer may not have to spend time figuring out which function to use if the name accurately describes the purpose of the function. This also applies to making these names searchable, it's a lot easier to find the correct line of code for &lt;code&gt;userPhoneUniqueId&lt;/code&gt; in a code base than just &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consistency in naming conventions is crucial, decide on a pattern of prefixes or patterns and stick to them across your code base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comments should add context.
&lt;/h3&gt;

&lt;p&gt;I know, I know... "never use comments." Again, this is impractical, there are times when comments add important context or insight into the nuances of a code base for future developers. So, yes use comments but make sure they add context to the code and are not just describing the obvious. Add enough meaningless comments in your code and future developers will ignore those comments because of the cognitive cost of trying to add all of them into working memory. Make your comments important and offer clarity to future developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use consistent error handling
&lt;/h3&gt;

&lt;p&gt;This one I think often gets ignored, but having a consistent and predictable way of handling errors and exceptions makes both debugging and future development easier. This should include gracefully handling errors and developing a system or guidelines for warnings, notices, etc. Define clear guidelines for error handling, including how errors should be logged, reported, and propagated throughout the codebase. You should also avoid suppressing errors, there is nothing more frustrating than realizing the hour you just spent pointlessly debugging code was caused by errors being suppressed earlier in the code. This should be something set in your code styles that are enforced through code reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid overly complex conditionals and control flow
&lt;/h3&gt;

&lt;p&gt;You can reach a point with complex conditionals and control flow that your code is almost impossible to follow. This makes debugging and maintaining that code harder as time goes on. Future developers will have to spend more time ensuring they understand the paths the conditional can take or the complexity of the control flow before actually writing or updating code. Conditionals and control flow should be as simple as they can be and abstracted to functions if need be. In some cases, you may reach a point where it makes sense to introduce a new design pattern to handle control flow or switch to assertions to handle conditionals. This all depends on the project, but this type of complexity creates huge amounts of technical debt as other developers move into the project and you move on from it. &lt;/p&gt;

&lt;p&gt;Readability also suffers from complex conditionals and control flow, your code just gets harder to understand. Remember, simpler code is often easier to comprehend and work with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid deep nesting
&lt;/h3&gt;

&lt;p&gt;This is similar to avoiding complex conditionals and control flow as it has negative impacts on readability and is harder for developers to quickly understand what is happening. You should avoid deeply nested loops and conditionals. This will make your code easier to maintain, easier to test, and more resistant to defects. The less complex the unit of code is the less likely it is to break when another developer comes and changes it. Deep nesting is a perfect example of an area that is hard for other developers to work with before spending time gaining context into the code. If you are nesting this deeply you probably need to refactor your code and abstract away some of the functionality into additional functions or look into alternative design patterns. Generally, I think at about 3 levels deep alarm bells should start going off that you may be reaching a level of complexity that is going to be difficult for other developers and it may be time to refactor. By 5 levels deep you need to start thinking about refactoring or optimizing your code. Breaking down complex logic into smaller, self-contained units not only enhances code maintainability but also promotes reusability and testability. Strive for flatter and more concise code structures to enhance the overall quality of your codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritize readability and avoid unnecessary complexity
&lt;/h3&gt;

&lt;p&gt;You're never going to be able to remove all complexity from code, that is not possible. The idea here is to emphasize well-written and efficient code that is easily understood by all developers over the cool one-liner that will take all of their cognitive load to understand. That level of complexity is usually not necessary and not worth the saved line or two. Code that is easier to read is easier to maintain, complex optimizations should be made only if it offers a substantial performance increase.&lt;/p&gt;

&lt;p&gt;Readability usually also goes hand in hand with using descriptive and meaningful names for variables and functions. It adds to the overall quality of your code and lowers the cognitive cost for new developers. Code is read more often than it is written, so spend the time making your code readable and understandable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separation of concerns
&lt;/h3&gt;

&lt;p&gt;Another important principle in software development is the separation of concerns. This practice will enhance your projects maintainability and flexibility for future upgrades or replacements. For example, you should isolate view templates from business logic or controllers. This separation allows for independent maintenance and updates, making it easier to manage and evolve each component individually. Another example is using data models instead of inline database queries. By decoupling database operations from other code sections, you improve maintainability and enable efficient updates to the core functionality or dependencies without impacting other areas of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use consts or enums for magic values
&lt;/h3&gt;

&lt;p&gt;This should go without saying, but it is way easier to understand a constant or an enum than a random integer or cryptic string. The cognitive load that comes with remembering an entire array of integers that all have additional meanings can make code impossible to efficiently work with. The readability and context improvements your code will gain from using constants or enums are worth the extra keystrokes every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow the principle of least astonishment
&lt;/h3&gt;

&lt;p&gt;Write your code to behave in a way that is expected and logical. Code that is counterintuitive, surprising, or acts illogically adds to the cognitive load of other developers, is harder to maintain, and is harder to troubleshoot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use design patterns where it makes sense to
&lt;/h3&gt;

&lt;p&gt;Overuse of design patterns can add complexity to your project where there doesn't need to be. If you are adding a design pattern to solve a one-off issue it may not be the best answer. An example would be adding a Command Pattern for a one-time save, this adds complexity without really any reward. If you are writing a text editor the Command Pattern might be a great way to reduce complexity and make your code easier to maintain and update. It just depends on the project itself. You should be aware of common design patterns and comfortable defining when they should be implemented. Most of the time this seems like something that is added on a refactor when you start abstracting away repeated functionality. &lt;/p&gt;

&lt;h3&gt;
  
  
  Encapsulate frequently reused code or code that causes side effects
&lt;/h3&gt;

&lt;p&gt;Code that is repeated more than a few times is probably a good candidate to be abstracted away. Sometimes this is a utility class and sometimes it is a base class. That depends on the functionality and the project itself. Code that is creating something in your system, like files, should be abstracted into a library or isolated functionality so it can be heavily tested and shared throughout the code base. &lt;/p&gt;

&lt;h3&gt;
  
  
  Keep important services modular
&lt;/h3&gt;

&lt;p&gt;At times you may need to break down your code into smaller, self-contained modules that facilitate easy testing, reusability, and updates. When appropriate, break down core functionality into modules that can be interacted with by other parts of your code through well-defined interfaces. This approach helps readability by having common solutions that are focused on a single responsibility, making them easier to learn and understand. An example of this would be creating an HTTP library or service for your app if you are making a lot of API calls. These modules are also easier to test effectively since they are defined in one place and not repeated throughout your code base. The last major benefit of keeping your code modular is that future updates to these modules only require you to update the module and not everywhere the feature is needed in your code base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't repeat yourself... too much
&lt;/h3&gt;

&lt;p&gt;Sometimes it is perfectly fine to repeat yourself to prioritize the readability of your code. It comes down to the project and the context of your code, but in some cases abstracting away some functionality into a function in a utility class because it is repeated a few times may make your code harder to maintain and learn. In some cases, this may add a level of complexity your code base doesn't need, just to say you followed DRY. So really, repeat yourself a few times and if you start getting to that point of repeating that unit of code more than 3 or 4 times start thinking about refactoring, if it makes sense with the context of your project. In some cases repeating something ten times may be perfectly fine. Think about using a random number built-in function for unique IDs, do you need to abstract that away? In some projects, it might be worth it, in others maybe not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Classes and functions should have a single responsibility
&lt;/h3&gt;

&lt;p&gt;This is more of a concept-based single responsibility and not a hyper granular abstract everything away into a series of miserable functions kind of single responsibility. Again, it depends on the project and context of your code so you need to find a balance in your project that works. Keeping classes and functions focused on a single concept makes it easier to work within those modules. These classes or functions can do multiple things, but they should all be related and critical to a core concept. For example, a function updating a user account might perform some validation or formatting of the user data, should this be broken out into multiple functions? I don't think it always should, it still is a critical part of updating that user account so it can happily belong in the same method. If that functionality becomes robust then it might be time to break it out into a validation or formatting function just for readability. &lt;/p&gt;

&lt;h3&gt;
  
  
  Type returns and function arguments
&lt;/h3&gt;

&lt;p&gt;This helps from a defensive coding, readability, consistency, and resiliency standpoint. Using types helps define what your code is doing and is part of telling other developers what it should be doing as well. It adds context to your code that you may otherwise need to add comments for developers to understand. It protects your code from future changes that may break functionality as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use testing to validate behaviors
&lt;/h3&gt;

&lt;p&gt;Implementation tests have a serious role in making sure critical code units are working as expected and that should always be a part of testing. Alongside this, you should be validating the behaviors of your code from start to finish. This helps ensure that the code is functioning as expected as a whole. This style of testing focuses on the outcomes, giving your code a certain state will result in a specific outcome. This way you know the entire user creation workflow, login workflow, etc work from start to finish as an end user would go through it. I'm not talking about integration or UI testing, just testing modules as a whole behavior. Testing like this should cover as many paths/branches as possible and should increase the confidence of other developers when they make changes to that code.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's a wrap
&lt;/h2&gt;

&lt;p&gt;Well, that's it, those are my recommendations for writing better code that is easier to maintain, read, and inherit. Not all of these ideas will work everywhere in every project you should always prioritize readability and what works best for the project in front of you. In the end, if you only can utilize one of these ideas in a project it may just make it a little easier for the next developer.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>coding</category>
      <category>code</category>
    </item>
    <item>
      <title>Testing For What Shouldn't Happen</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Mon, 01 May 2023 05:14:16 +0000</pubDate>
      <link>https://dev.to/dvnc0/testing-for-what-shouldnt-happen-2pnb</link>
      <guid>https://dev.to/dvnc0/testing-for-what-shouldnt-happen-2pnb</guid>
      <description>&lt;p&gt;I know it sounds weird, why should I test for what isn't happening if my tests are passing? The simple answer is that like the rest of your code, your tests can have bugs. Likewise, the logic that implements pieces of your code can introduce unanticipated behaviors that your tests are not designed for. Let's set the scene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your made-up app is a to-do list that allows for family accounts with multiple users.&lt;/li&gt;
&lt;li&gt;You have an improvement to allow a secondary account owner.&lt;/li&gt;
&lt;li&gt;Your primary and secondary owner's contact information is updated through their user settings but also updates data in the main account settings&lt;/li&gt;
&lt;li&gt;Like always features need to be done yesterday and your backlog is breathing down your neck.&lt;/li&gt;
&lt;li&gt;Your tests are mainly implementation tests that focus on validating individual methods and assume some of the data state coming into them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some Mock Code to Set the Stage
&lt;/h2&gt;

&lt;p&gt;I'm going to write these examples in PHP since that is my daily driver, but this can happen in any code base and to any project. Your code starts off looking something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User_Account_Logic&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Logic_Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateUserAccountInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userDataIsNotValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userShouldUpdateMainAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateContactInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Successfully updated user information!'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;userShouldUpdateMainAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'account_type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main_Account_Logic&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Logic_Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateContactInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'account_type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateMainAccountContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Somewhere in your unit tests....&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User_Account_LogicTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUserAccountMock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMockBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;disableOriginalConstructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onlyMethods&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'updateContactInformation'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'updateContactInformation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;User_Account_Logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testThatFamilyMembersCanUpdateTheirSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getUserAccountMock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMockBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;disableOriginalConstructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onlyMethods&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'save'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'save'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateUserAccountInformation&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'foo bar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'account_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MEMBER&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing too crazy, you're updating a user, and if they are the primary you are updating the main account information as well. You have unit tests with a handy helper function to build your mock user account class and everything is working wonderfully. You don't mind that the helper assumed the return of the main account code because you are implementation testing that as well so what could go wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes wrong
&lt;/h2&gt;

&lt;p&gt;You're in a rush to get this feature out and you make some changes allowing a secondary owner to update the main accounts information for the secondary owner. This owner can do everything the main account holder can and this is a big feature that has been asked for. So one of the changes you make is the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User_Account_Logic&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Logic_Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateUserAccountInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userDataIsNotValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userShouldUpdateMainAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateContactInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Successfully updated user information!'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;userShouldUpdateMainAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'account_type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SECONDARY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main_Account_Logic&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Logic_Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateContactInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'account_type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateMainAccountContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateSecondaryAccountInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_information&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In these files, that's all you do is one quick change of a &lt;code&gt;===&lt;/code&gt; in the &lt;code&gt;User_Account_Logic&lt;/code&gt; method &lt;code&gt;userShouldUpdateMainAccount&lt;/code&gt; and added a call to update the secondary account contact information in the &lt;code&gt;Main_Account_Logic&lt;/code&gt; class. What you just did was make any account user other than the owner the secondary account owner. That quick typo of a &lt;code&gt;&amp;gt;&lt;/code&gt; instead of a &lt;code&gt;&amp;lt;&lt;/code&gt; just gave any user the ability to update the main account secondary user to their account. &lt;/p&gt;

&lt;h2&gt;
  
  
  Testing will catch it... right?
&lt;/h2&gt;

&lt;p&gt;No, it won't because you don't update your tests because they still pass, you just changed which accounts hit the update main account method so nothing should fail. Since you're focused on implementation testing and not behavior testing you mock the response from &lt;code&gt;Main_Account_Logic&lt;/code&gt; and &lt;code&gt;Main_Account_LogicTest&lt;/code&gt; assumes the state of the data it is given so all your tests pass. Good enough, push that bad boy live.&lt;/p&gt;

&lt;h3&gt;
  
  
  How could testing have stopped this?
&lt;/h3&gt;

&lt;p&gt;You could have added one thing to prevent this test from passing when it shouldn't. That one thing is testing for what shouldn't happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User_Account_LogicTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUserAccountMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$main_account_hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMockBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;disableOriginalConstructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onlyMethods&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'updateContactInformation'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;exactly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$main_account_hits&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'updateContactInformation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;User_Account_Logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Main_Account_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Mock_Main_Account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$Mock_User_Logic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testThatFamilyMembersCanUpdateTheirSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getUserAccountMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMockBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;disableOriginalConstructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onlyMethods&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'save'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'save'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;User_Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Mock_User_Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$User_Account_Logic&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateUserAccountInformation&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'foo bar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'account_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User_Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MEMBER&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding that &lt;code&gt;$this-&amp;gt;exactly(0)&lt;/code&gt; would have caused this test to fail. PHPUnit would have reported an error since you expected that method to be called 0 times and it was called once. Then you would have caught your mistake and you would have prevented your user's son who has a terrible password and gets his account compromised from giving the attacker owner-level access to your user's account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Making sure that you also test for what shouldn't be happening can help improve the reliability of your tests and catch bugs earlier. Testing is not only about what should happen but what should not happen to your system and overlooking what shouldn't happen can end up with code that acts the complete opposite of how you assumed.&lt;/p&gt;

</description>
      <category>php</category>
      <category>unittest</category>
      <category>testing</category>
    </item>
    <item>
      <title>Behavior-Driven Testing with PHP and PHPUnit</title>
      <dc:creator>dvnc0</dc:creator>
      <pubDate>Fri, 28 Apr 2023 05:15:12 +0000</pubDate>
      <link>https://dev.to/dvnc0/behavior-driven-testing-with-php-and-phpunit-pgm</link>
      <guid>https://dev.to/dvnc0/behavior-driven-testing-with-php-and-phpunit-pgm</guid>
      <description>&lt;p&gt;Unit testing is something most developers run into at some point in their careers. For some developers, it is there from day one, for others it comes and goes. In my experience, it seems like most developers dislike the unit testing part of writing clean and efficient code until they realize they are writing their tests wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing tests for days
&lt;/h2&gt;

&lt;p&gt;Most developers will focus on writing implementation tests for every method they create. This is tedious, requires constant updates, and is inefficient. When you are writing three or four tests for each method it is easy to dread writing those tests, but most developers are never taught any other way to write or think about those tests. This same implementation-based testing can lead to bugs and assumptions that your code will perform correctly with any given state. Don't get me wrong implementation tests have their place, but they should be very technical and focus on validating the specific functionality of critical methods in your code. Implementation tests are rigid and follow the instruction-based thinking of programmers, testing that every step along the way behaves correctly, but not necessarily testing that given a specific state the code as an entire body behaves correctly. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Behavior-Driven Testing
&lt;/h2&gt;

&lt;p&gt;Behavior-Driven Testing (BDT) is the opposite of implementation testing, it looks at the code from the outside in describing the behaviors of the application. Designing these tests can even include Stakeholders and Project Managers since they help create a shared language and understanding of the functionality. You can think of BDT as a method to describe and test what software should do by defining features, and scenarios, and enforcing clean and modular code that is easy to test. Additionally, BDT generally creates better code coverage (defines tests for more branches and paths), aids in creating clear documentation, and improves code quality by allowing devs to focus more on writing code and not testing it or refactoring tests. Often behavior-driven tests do not require modifications when the code is changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  BDT vs Implementation
&lt;/h3&gt;

&lt;p&gt;Comparing the two BDT follows more of the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I order a package, there are no holidays and no weather events.

Given my order is successfully completed

It should arrive on time and without damage.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You care about the outcome of the total process, not validating every step of the process. Implementation testing is similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I place my order
It is pulled from the shelf
It is put in a box
A Shipping label is created
The box is put on a truck
The truck travels 30 miles to the shipping Partner
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You cover all these with BDT but the key difference in an implementation test is you are covering those methods in isolation from the rest of the application flow. This means you have to &lt;em&gt;assume&lt;/em&gt; the state and the responses from other methods. You may write 10 tests for one of these methods, but what is the possibility you miss a change of state that happens before this method is ever reached? If you are testing the departure time of the truck and you miss a delay pulling from the shelf how does that change your test? Implementation testing forces you to write specific tests for each method for each possible branch/path your code takes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thinking about it without the programmer's glasses
&lt;/h3&gt;

&lt;p&gt;An example of BDT and Behavior-Driven Design would be making a board game with your friends or family. You have the main idea of the game but need to ensure that when given a specific state when an action occurs that the result is predictable. So you may start defining behaviors &lt;code&gt;If a player has won 3 battles when they complete board section A then they should be awarded 100 XP&lt;/code&gt;. This clearly defines a testable behavior that should be consistent with the given state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thinking about it as a programmer
&lt;/h3&gt;

&lt;p&gt;In terms of programming BDT can be broken down into a fairly simple sentence &lt;code&gt;Given a specific state when an action or series of actions are completed the outcome should be predictable and repeatable&lt;/code&gt;. What exactly does that mean and how can we define behaviors? Feature files and scenarios are tools used in BDT to define behaviors in a common language (usually &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt;). Let's use authenticating users as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FEATURE: User Authentication
  As a user I want to be able to log in and out of my account

  SCENARIO: Successful login
    GIVEN I am on the login page and supply the correct credentials
    WHEN the login form is submitted
    THEN I should be logged in
    AND a JWT should be returned
    AND I should be redirected to my home page

  SCENARIO: Failed login
    GIVEN I am on the login page and use incorrect credentials
    WHEN the login form is submitted
    THEN I should not be logged in
    AND I should be given an error message
    AND I should not be redirected
    AND I should not be allowed to manually access my home page

  SCENARIO: Logged out
    GIVEN I am logged in successfully
    WHEN I hit the logout button in the main navigation
    THEN I should be logged out
    AND the JWT destroyed
    AND I should be redirected to the home page
    AND I should not be allowed to manually access my home page

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see in the example above the different behaviors of User Authentication are described from the perspective of a user. This allows us to test the entirety of the authentication functionality from three behaviors. Another way to think about this is testing that given a beginning state, the outcome will always be the same. In the example above given correct user credentials the outcome should always be as defined in the &lt;code&gt;THEN&lt;/code&gt; section. &lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Behaviors
&lt;/h3&gt;

&lt;p&gt;When it comes to testing behaviors you should mock as little as possible to ensure all the code from start to finish is correctly functioning, this helps avoid the need for implementation tests later on. Some things to mock would be database calls or HTTP requests. If you are using Adapters for these areas or areas like third-party libraries mocking them becomes even easier. Overall, the less you have mocked the more you can trust your behavior tests are correctly representing the outcome of the executed code. &lt;/p&gt;

&lt;p&gt;You can use tools like Behat which are designed specifically for BDT and make use of Gherkin feature files to help automate test building. In most cases, you are probably using PHPUnit so we can start there. You have already done some of the work in defining your scenarios, these translate to test methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit\Framework\TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAuthenticationTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testThatGivenCorrectCredentialsTheUserIsLoggedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;thestThatGivenIncorrectCredentialsTheUserIsNotLoggedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testThatTheLoggedInUserCanLogOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three tests will cover the possible behaviors of your authentication workflow. Assuming that you have a controller that handles the login we could write something like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit\Framework\TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nf"&gt;App\Controllers\Login_Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Core\Database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAuthenticationTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testThatGivenCorrectCredentialsTheUserIsLoggedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$Login_Controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Login_Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$Mock_DB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMockBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onlyMethods&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'fetchRow'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$Mock_DB&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fetchRow'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;willReturn&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'somehashedpassword'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nv"&gt;$Login_Controller&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Mock_DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$Login_Controller&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;logUserIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertArrayHasKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jwt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above we are just passing in a mocked database class and letting the rest of the code run as it should. This allows us to test that all of the code in that behavior is functioning correctly to produce the desired outcome. The database class would likely be implementation tested since it is a critical component to the application so we can feel comfortable mocking that. Now once you create tests for the remaining two scenarios you will most likely cover all, or close to all, of the code involved in your user authentication process.&lt;/p&gt;

&lt;p&gt;You may need to add more scenarios for disabled or suspended accounts etc but this is just a gist to show the strength of testing behaviors. The best part is if any of the code changes that are used for the given scenario you may not have to update the tests at all. This is because the behavior is tested as a whole with a starting state that produces a repeatable result. If the result has changed there is likely an error or a change in the process, like renaming the &lt;code&gt;jwt&lt;/code&gt; key. Compared to an implementation-based approach if you changed any of the code the authentication scenario depends on you would need to update each test for the change. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Behavior-Driven Testing is another tool in your developer belt that can help you write fewer tests, spend less time refactoring tests, have greater confidence in the reliability of your tests, and help define a shared definition of the features of your application.&lt;/p&gt;

</description>
      <category>php</category>
      <category>bdt</category>
      <category>phpunit</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
