I wanted to share just a few general programming tips/ideas, from whatever I've learned and experienced.
Coding iteratively: There are so many aspects to software development, that it is not possible to attack all of them in one go, so code must be written iteratively, and I have done so from the very beginning. My process usually looks something like this (and not always in this order):
-
Get it working for the simplest possible use case, one way or another, no matter how ugly, inefficient or redundant. This is to prove that the attempted goal is feasible in the first place ... some may not be. A "proof of concept".
-
Reliability: Think about the less likely cases (e.g. invalid user input), and edge/corner cases (i.e. unlikely but possible cases) ... for instance, the user doing something totally weird with the app.
-
Efficiency/performance: analyze loops, method calls, look at how much memory is being allocated (data structures). Are your data structures best suited for the purpose ? Are you doing real-time data lookups for the user ? Think about O(1) performance for lookups if possible. Are you doing background tasks ? Data structures have tradeoffs ... Swift Dictionary objects, for instance, give you O(1) average lookup time, but cannot and will not guarantee iteration order when you later want to read all the objects in them. WTF !!! So, tradeoffs ... Do you need object uniqueness (no duplicates) ? Set.
-
Thread-safety: How many, and which, threads will access this piece of code ? Am I modifying any shared data here ? If so, are my data structures thread-safe ?
-
Reduce redundancy ... see if the code can be factored out into a private helper or public util. Or, look for a 3rd party library that does part or all of what you just wrote.
-
Extensibility: If I were to augment this feature in the future, would it be easy ?
-
Readability and code commenting: Comment, at least, the parts that are not obvious to the reader (like any complex algorithm steps or obscure programming tricks). You'll thank yourself a month later, when you revisit the code. Each class and each public method/function should have at least a one-liner explaining its purpose. Whenever assumptions are made (e.g. a function parameter must be a non-nil positive integer between 1 and 10), document those assumptions in the code. Otherwise, no one (not even you) will remember them months later.
- Documentation: Provide your end users/clients with the basic knowledge of how to access the public interface of whatever code you write. This could be an entire app/service or just one class. The client could be a customer or the dude in the cubicle next to yours who is going to write a layer on top of your code. Make the public interface well-defined and unambiguous. Again, document all assumptions.
-
Security/scalability: This doesn't necessarily apply to all apps, but let's say you're working on a commercial web app that serves tens of thousands of global users. Security is self-explanatory, and scalability then becomes a key part of your programming process. Each choice of data structure or control structure can make a difference. Load testing (another good thing to look up) is making sure that your production system has the performance and resources to serve its current user base + a safety margin for growth.
-
3rd party code: Has this logic I'm writing been implemented by others and well-tested before ? i.e. in a 3rd party library ? For instance, in the Java world, the Apache project (and later Google code) was the go-to for open source libraries for doing anything and everything in Java. Are there any known issues with this library that will introduce bugs into my app ?
-
Testing: There's unit testing, integration testing, manual testing, load testing. Figure out what kinds are needed for your app. In the real world, you will likely use all 4 (and more). Load testing is particularly interesting and I was heavily involved in it.
Tradeoffs: The occupation of software development, like civil engineering or almost any other field, is largely about tradeoffs or compromises. Whenever you make a decision, you will likely achieve certain advantages while sacrificing others. Always keep this in mind. Examples follow:
-
Code readability vs conciseness/efficiency: I've come across this tradeoff almost on a daily basis when developing. This is best explained with an example.
Code:
// One way of doing it
let randomIndex: Int = random(userPrefs.minInt, userPrefs.maxInt)
let randomTrack = playlist.getTrack(randomIndex)
player.play(randomTrack)
// Another way
player.play(playlist.getTrack(random(userPrefs.minInt, userPrefs.maxInt)))
This is an exaggerated case, but sometimes, readability is more important than conciseness, esp. when working in a team environment where others will look at your code.
The language you're using, and Swift is a great example, may provide a lot of syntactic sugar to help you code concisely, which is great, and you will be tempted to reduce your code to one liners, but don't ignore readability completely.
If you choose conciseness over readability, code commenting becomes all the more important.
-
Sweeping the dirt under the rug: Sometimes, you will rush to refactor one piece of code you deem ugly, and not realize that by cleaning one piece of code, you have created another piece of code that is 10 times uglier. I encountered this with what I saw in the IINA code with WindowControllers all over the place. In order to blindly adhere to "best practices", there are 10 short and sweet XIBs, but parts of the source code are ugly and violate fundamental OOP principles. The dirt has been swept under the rug

It didn't disappear; it just moved from one place to another, and somehow even managed to grow
-
Sacrificing performance (sometimes) for a better user experience: This seems like a contradiction. Usually, user experience is directly tied to better performance, but how can you forget the example of the GIF animations you despise so much
Now, of course, it is important to note that a GIF animation causes a CPU spike; simply ignoring it would not be prudent. But, when it comes to making a decision of whether or not to allow it at all, you have to step out of the shoes of the programmer and put yourself in the shoes of the users. Some users would prefer that it be totally disabled, because it gets annoying and/or CPU performance. Other users (e.g. iMac Pro users with 32 cores) may like the visuals and prefer it be on all the time. If you assume one way or the other, you're making the decision as a programmer, not as a user. "This takes up a lot of CPU so let's put a boring little text symbol there and do away with it." - this is merely a programmer's perspective, because we know that CPU time should not be wasted. Zoom out and look at the bigger picture. What would your users want ? Improved CPU usage, longer battery life, and also a nice animation when they're in the mood ! Base your decisions on that.
Of course, this is why any software development shop of reasonable size has a UX department
-
Time vs the "best" solution: Playing with sample XCode projects from Ray Wenderlich and finding 15 ways to dock two windows is a lot of fun and when learning, time is no object
. But, in the real world, time is a very real thing, and often times, you will be forced to implement a solution that you're not the biggest fan of, and move on to the next task. You'll need to pick a candy bar and rush back to the train because the train is about to depart on its way to the next station. And that is ok, if you can find a balance/compromise that works.
In the real world, there is a time and place for research and learning, and there is a time and place for getting things done. The dudes with the PhDs are usually the ones in R&D exploring 100 different protocols for inter-process communication. Head over to the Engineering building across the campus and you'll see developers building and deploying releases to a production environment and responding to bug reports. I was in the Engineering building, of course
Both are great to have, and there needs to be a balance between the two. R&D provides valuable input to Engg on how something can be done best, and Engg gets it implemented and shipped. Too much R&D and you get nowhere in the end. No learning and you ship solutions that suck.
Over-engineering is a common problem among developers arguing over for loops. Not every app is a NASA rocket saving the Earth from an asteroid, and not every for loop deserves an hour of debate, so learn to manage time and give every problem its due time and attention. This may not make any sense to you right now, because you're in the "lab" and everything seems important, and you don't have to worry about deadlines, but get out in the real world and you'll see what I mean.
In any case, balance is always a good thing to have. You can spend a week on figuring out the "best" window docking solution, or you can spend 2 days on it, and get 5 other features implemented
-
Zoom out: Being too "zoomed in" is another common problem among developers. They get too focused on their tiny problem that they lose sight of the bigger picture/context. This is with anything in life, really. It helps to step back and see the bigger picture of what you're trying to achieve and how. Things become clearer then.
And, with so many different ways of doing something, esp. with programming, it can be confusing to have to pick one way of doing things. It always helps to "zoom out" from your little class or module to the app or project as a whole, and ask yourself what the ultimate goal(s) are. Who are my end users ? What do they want from this app ? Is this a standalone desktop app or is it simultaneously serving a million online global users ?
When stuck on a problem, don't always insist on jumping over the hurdle. Sometimes, it is best to walk around the hurdle.
Yes, we need guidelines and best practices for structure, but don't let them constrain you into blindly adhering to them. Apply them in a larger context. First of all, realize that there IS a larger context. Then, apply those details within it. Don't confuse personal preferences or even "best practices" with what is "best". Often, what is "best" has a lot more to do with the clients and how they will use the app, than with the programmers. And sometimes, the "best" solution is simply the one that makes it to the customer on time
Hope this helps.