Fix Bugs in Your Swift Code
We use the squash, splat and crush analogy from insects when talking about bugs. This is with good reason: Thomas Edison wrote about bugs during his hardware engineering travails and this means the term is probably here to stay.
Get used to it, or bug off!
Deal with the bugs (Approaches)
Clean your area
One way to make your code bug-free is to avoid writing them in the first place!
- Simplicity and clarity
By testing (use this Swift TDD guide) your code is much less likely be to bugged as it is tested before ever getting close to production.
The works hand-in-hand with modularity which and making sure that your code is written in separate, testable chunks. You might have chosen MVC, MVVM, VIPER or some other architecture for your project — and there are advantages and disadvantages to each.
The third pillar of of well-written non-bugged code is simple clear code. This minimal code should be created with the eventual reader in mind (that is, another programmer should be able to pick up your code and understand it).
//TODO is fine for missing functionality, but code that is incomplete should not be checked in — and is a great use of annotation in your code!
Don’t ignore warnings
Warnings are there for a reason and should not be suppressed (Ok, you can suppress some without consequence). Using a linter helps to point out your errors before they grow and become real problems that can stop a release or mean that you’ll be working late into Friday night.
Another way to put this is:
Do it once — do it right.
I’m sure this won’t be a problem to you, but of course you’d never ignore a warning in the first place, would you? Some even put their own errors in their code
Consider ignoring your problems
It seems counter-intuitive, but you can ignore some of your problems and hope they go away. Unprofesssional? Sure. Not-recommended? This is tricky.
If you have a bug that affects a very small number of users, and may require a potentially inordinate amount of time to fix it may just be justifiable to leave this bug (even in production code).
As an approach, the decision to go with this is probably above the pay-grade of a coder. That is, this should be a team decision and the team should decide whether leaving an extremely difficult to fix bug is…well…too difficult.
As a repeated strategy for an individual coder ignoring badly coded solutions may be a sign of incompetence. That is, use this technique with an abundance of care.
Use an issue tracker
There are many different offers of issue trackers for use in teams and organisations. This enables the issues to be triaged and organised into tiers of importance and urgency so the most urgent and important issues get checked first.
Bugs (beyond the initial ones picked up in unit testing) should be logged so an understanding of issues and when they arise (as well as why they arise) can be formulated.
Even if you are working on your own, keeping a log of issues that have been encountered can be useful, and GitHub even has a suite of features that can help you out!
Check under assumptions
One of the most important things in coding is checking that the ideas you have and the way that you think things should work is accurate. This can seem simpler to say than to do and the long term strategy is to understand the technology you are working with from the capabilities to the implementation of the same. In the short term some light rubber duck debugging.
You might like to do the following — record assumptions and theories as such for each issue, and then test them when they need to be. How? Here are the definitions:
Assumptions: The preconditions for a problem
Theories: Ideas that you have that can be tested sequentially in the pursuit of a solution
Block of the nest (narrow down the problem)
Most IDEs give you some idea of what might be going on in your code. You can step through any given problem using breakpoints and perhaps even more features that can help you find out where the problem is in your code.
If you’re using TDD then you probably already have well-tested code that you are slotting together to make a whole. Are you sure that your units are well-tested? Now is the time to take a look. Use your debugger to ensure that your variables are what you expect them to be at any given point.
Most of all; rule at general possibilities for error and wind them down into specific opportunities for investigation. This isolate-test-fix strategy should get you closer to a solution than when you started.
Go back to when everything was fine (version control)
You are using GIT, right? If not you have some method of version control? So when did this issue first appear? When you can narrow down when the problem appeared you can can use this to enhance your investigation. Since you know something about the origin of the error you can use this to help you find the solution. Make a new branch and then add back in code at will. You’ll hunt this down, and get the solution right!
Create a MVP
Create a test program to reproduce the error. This might work where you (unfortunately) don’t have modular testable code. You can then separate out the code that is likely to have made the issue, and then add sections back into the program until the problem manifests. This would mean that you’ve found out the original issue and can go and fix it!
Check the underlying design
This one is perhaps the most painful conclusion. The project has not been built on good foundations, and perhaps the whole initial design is not suitable for the project. This can also happen when the project is changed during the implementation stage and then, essentially, can be said to not be the programmers fault.
In any case though, it needs to be fixed. If the underlying design is an issue it perhaps does need to be changed, but the costs for doing to may be great which means that you must be sure that the time spent to fix this problem will be worth the rewrite in the end.
If you’re in this situation, I hope you found it early…
Even if you follow the approaches detailed above, you are still likely to run into problems and issues (and, dare we say it, bugs) in your code.
Recording these also requires some finesse and understanding of the software development approach.
Many bug-fixing tools have a workflow integrated into their interface, but remember to record the following:
- Reproducability: Can it be reproduced?
- Severity: How important is it that the bug be fixed?
You might also have the ability to save crash logs, and this might help you when you are trying to solve a problem that has just come up. After all, you want clarity on the problem and work to solve the issue is easier when you have the full information.
When all is lost (Strategies)
One of the greatest problem solving strategies is the following:
- take a break
- have a stress-free sleep
It seems almost ridiculous. However your mind works much better after a rest, and you should leverage that in your problem solving journey and using the fact that your mind can ruminate on things when it’s concentration is elsewhere is a great strategy for trying to solve that unsolvable problem.
Bugs away (Conclusion)
We all should write perfect code, all of the time. There should never be any bugs, and the software should conform to the requirements and pass User Testing at the first time of asking.
Unfortunately, software development is an inherently human process, and humans do unfortunately make mistakes. Unfortunately we do have responsibility as developer to hunt down those bugs and squash them — preferably before they ever existed.
I hope this article has given you an idea of how this can happen in a professional environment.