When I began my professional career, I worked in a company where using third-party frameworks wasn’t allowed.
We had to write all our features from scratch, building them in such a way that they could be reused in additional projects as our own internal frameworks.
It’s a weird policy, but it might have been one of the best learning tools I've ever encountered as a software developer. And if you think writing features from scratch isn’t feasible, I can assure you it is. In spite of the extra work, we always managed to deliver on time, and the business was a profitable one.
That said, I completely understand scenarios where this isn’t affordable. In fact, at another job I had, the protocol was the exact opposite: if there was a third-party framework that did what we wanted that we didn’t make use of, we’d be reprimanded for wasting time and resources in reinventing the wheel.
That was also a weird policy, but I learned a lot from using third-party frameworks — doing so exposed me to other people's code, which in turn provided me with a lot of additional knowledge and experience. I remember so many aha moments from that time.
Having lived both extremes many times in my career, I’ve discovered that striking a balance of the two often gives me better results in my work.
On one hand, I strongly believe that doing things oneself is the best way to learn, and writing everything from scratch taught me how to build frameworks, reusable modular code, good APIs, and much more. It pushed me to learn how to code efficiently and solve any problem, particularly in the early stages of my career when the learning curve was steep.
On the other hand, opting to use third-party frameworks helped me quickly create high-quality solutions, enabled me to learn from experienced developers around the world, and improved my skills as I encountered different schools of thought in software development.
Both of these ways of working are extremely valuable, and my wish is that every developer is given the opportunity to experience them too.
Of course, not everyone has the freedom to write from scratch at their job, and when that’s the case, trying it out with side projects is often a good way to learn. This is exactly what I did.
Back in 2009, as an exercise in learning how to write iOS apps, I wrote all my Objective-C implementations for networking and JSON, XML, and HTML parsers.
For persistence, I've written SQLite and Core Data wrappers. I even wrote my own text-based file storage scheme, which may sound silly, but it was a simple solution for many problems, and ultimately it resulted in the satisfaction of my clients.
We're so accustomed to using known technologies that we often forget how, many times, a framework like Core Data is just overkill, whereas a custom solution is cheaper and quicker to build and maintain.
To be clear, I also use Core Data from time to time and it has provided tremendous value for more complex projects. For example, it helped keep memory footprint low and maintain a responsive UI thread, even in the old days when I used to test my apps on an iPod Touch (3rd generation). These are things that would’ve been difficult and expensive to do with a custom solution, especially when dealing with large amounts of data.
So, how do you find a balance between these two extremes?
Writing things from scratch every time is wasteful, but writing modules and reusing them in the domain of your projects can be a good way to go, especially when you're learning.
Meanwhile, ignoring third-party frameworks is also wasteful, and doing so may result in you facing many challenges that have already been solved and validated in other people's projects.
Remember Parse? I do.
We have a difficult announcement to make. Beginning today we're winding down the Parse service, and Parse will be fully retired after a year-long period ending on January 28, 2017. We're proud that we've been able to help so many of you build great mobile apps, but we need to focus our resources elsewhere. —taken from their blog
Parse “moved on,” but how easy was it for their clients to move on too?
I'm sure many of you were affected by Parse closing down, and it was likely an important lesson about relying too much on a third-party service.
But it doesn’t always have to be as extreme as a service shutting down. For example, what happens when the service you’re using still exists but no longer fulfills your needs?
For a long time, my favorite go-to framework for REST applications was RestKit — not so much for the REST side of it, but rather for its powerful mapping framework that allowed me to create my application domain models from a JSON/XML source without leaking that responsibility to the model classes.
I wish I could’ve made use of just that part of the framework, but its architecture made it very difficult to separate things.
It also struck me as weird that I couldn't create my own client implementation because many of RestKit’s classes were subclassing AFNetworking components (it was built on top of AFNetworking 1.x.), so the project was totally dependent on AFNetworking. Even so, I decided to accept those shortcomings and use it for the networking layer too.
And although I decided to still use it to save me a lot of development time, I hid it behind a protocol that wouldn’t leak that dependency to my application.
My applications only talk to API protocols and expect back application domain models that don't inherit from Core Data, RestKit, or any framework dependency. That makes the applications extremely easy and lightweight to write, maintain, and test.
Then came the day when Apple introduced NSURLSession, and one of the projects I was working on needed some of the new functionalities, e.g. background downloads.
The RestKit community started a long discussion about how to port its client architecture to support this, but as of today, it hasn’t yet been done. AFNetworking 2.x was released with NSURLSession in its core, which would seem like it would solve RestKit's problems, but it was just too hard to make the switch to this new version because of all the breaking changes involved.
Luckily, because I had my own architecture in place, it was easy to move my clients’ projects to a new networking implementation.
And wouldn’t you know, the transition was easy and had no regressions. None. Zero.
I find that quite remarkable.
RestKit’s mistake was to mix too many responsibilities without the proper separation of concerns, and it paid the price. The image below shows a proposed architecture for a networking layer that could have saved RestKit.
(To be clear, I too have done the same in the past, and I paid the price as well. You can be certain I'll share more about this another day so you can learn from my mistakes.)
Stories like what occurred with Parse and RestKit reinforce the importance of a modular approach and how we as professionals must constantly be aware of it. For me, this approach is no longer a question but more a duty I have to my clients and myself.
If you’re wondering where to begin with a modular approach, I recommend starting small to familiarize yourself with the idea before using it on a larger scale. Define your protocols to separate the responsibilities of your domain problems and write your own implementations at first. When appropriate, choose a good third-party framework and hide it behind your protocols so that you can develop, maintain, test, and deploy them separately. With such an approach, it’s easy to swap the implementation with a new one, and it makes the modules reusable.
You can also focus your energy on refactoring the one module (and we all have one) that drastically decreases your productivity and causes so many headaches. Try adding some tests with that setup too. You’ll quickly discover how delightful it can be.
Does doing this take more time? It might seem so. However, based on my own experience, it’s easier to adapt to changes; it allows you to collaborate with peers by sharing responsibilities without getting in one another's way; and much of the code is reused in many other applications in the same domain, making it super flexible for deploying on different platforms, like macOS and tvOS.
Business needs are always changing and developers normally fear those changes. They understand the risks of change but often can't find a way to protect themselves against it.
Have a look at the projects you're working right now and find the vulnerable dependent parts. Can you easily swap them? Can you develop, maintain, test, and deploy them independently?
There's a gap in there, and in that gap there's a huge opportunity to generate value and take your career to the next level. Mastering techniques that help you create modular and decoupled components is the key, and we’ll talk about this more in future blog posts.