When I was introduced to object-oriented programming, inheritance was one of the main principles taught. Inheritance is a good concept to learn. There are type relationships where something “is-a” child type of an existing parent type.

However, composition is a much more useful concept. When something “has-a” typed member, it is a more useful relationship.

For instance, instead of caring that types inherits from an Animal base implementation, the concern should be that individual types implement a set of methods (hence implementing an interface). For types which are composed with other member types, you can implement the interface by adapting to the interface and delegating to a composed member instance.

Adapting to an interface (whether strongly defined or just by “duck typing”) using delegation gains the benefits of re-using a method implementation without the downsides of inheritance.

The downsides of inheritance include (in most languages) only allowing single parent type inheritance, base parent types which cannot be easily changed without affecting child types, and potential complexities in multiple layers in inheritance type hierarchies.

Inheritance comes with many limitations and a few benefits, but composition is ultimately what should be preferred.

If you ever use a framework or library, the multi-threading model used in a dependency is one of the most important things to understand.

When I first heard this advice, I interpreted this to mean that I should be careful whether functions were thread safe. The question on my mind was: could I safely call methods of an instance on multiple threads concurrently? Afterall, when someone explains concurrency, they usually describe when two threads are trying to invoke the same method.

Later, I learned this is fundamentally the wrong question. It is rare that a framework or library has code that guards an individual method or an object instance. Most third party code either leaves all concurrency issues for you to solve or provides a way for you to call code on a designated thread.

If there are no concurrency guarantees, the library is easy to use in practice. You can assume that the third party code is like yours. Whether you guard individual methods, components, or entire systems, you can extend your concurrency patterns. In a way, having no concurrency guarantees is simpler to reason about and integrate.

When a framework or library requires you to use a designated thread, it often makes your calling code more complex. For instance, most GUI frameworks require you to call framework methods on the “main thread”. Therefore, every time you are about to call a GUI framework method, you have to ask yourself: is the current code being called on the main thread? If work is being done on a background thread, you have to somehow dispatch the GUI framework method call back on the main thread. Usually, dispatching code on a designated thread is easy, but the difficulty is remembering that you need to make the switch.

When frameworks and libraries require callers to use a designated thread, it is serially queueing all the work so there are fewer possible concurrency issues. Instead of complexity in every method ensuring all the framework code is multi-thread safe, it can simplify the framework code.

Changing perspectives from worrying about concurrency in individual instances to entire components is one of the things I appreciated learning from experience rather than from school.

Most of the apps I prefer have a few common characteristics. They keep up with the latest OS features, are designed with “best practices” in mind, and are native to the platform (e.g. they don’t have to be built by the OS provided frameworks but they must look and behave like a native OS app).

If I were to list my current top 5 iOS apps, they would have to be the following:

1Password

1Password is my go-to password app. I have a subscription, and it is truly a best of class app. It keeps up with the latest iOS features like iOS 12 Password Autofill. If you don’t have a password manager, it is the one universal recommendation I can make. 1Password also is avaiable for macOS, Windows, and Android.

OmniFocus

OmniFocus is perhaps the app I use the most. With OmniFocus 3, the switch from Getting Things Done contexts to more useful tags, it is everything I want in a to-do app. It handles complex repeats, has a special Today tag for items that aren’t due but you want to work on, etc. It is a bit pricey, but the major version upgrades are only every few years. While there are obviously many competitors, OmniFocus keeps up with iOS and macOS features. OmniFocus offers many complex features such as perspectives which helps manage my to-do list while still being usable.

Overcast

Overcast is my podcast player of choice. The Smart Speed feature which basically skips silence, and the Voice Boost feature which helps make sure the sound is loud and clear.

Soulver

Soulver is a notepad calculator. While I actually use the macOS version much more than the iOS version, I still find the iOS version useful every now and then. Basically, it is the way a calculator app should be. Once you try it, you won’t ever want to use a “normal” calculator ever again.

MindNode

MindNode is a mind mapping app. If you’ve never used a mindmapping app, it is a great way to quickly jot down, visualize, and expand on ideas.