This post is part of a series of posts designed to make you think about your own design philosophy on different topics. I will not be laying out direct examples to prove my own thoughts and ideas. It takes me two or three days in the classroom to do that and it’s why I think my classes are so special.
My goal is to get you and others to write the next set of blog posts to prove or disprove these ideas. If you do, then you will be starting a journey of developing your own design philosophy and how brilliant is that.
Can you provide a clear explanation to when a piece of code written in your favorite language is readable? Your answer must go beyond the idea of “code that is easily understood”, because that is too subjective. What is easily understood by you may not be easily understood by me.
Here’s another question. How do you know when an API you are working on is done and you can move on to the next API? If you answer “when you have enough test coverage”, that is not enough. That answer leads to a whole new series of questions about when you do or don’t have enough test coverage. Your answers must be measurable and easily smelled out in code reviews.
Before I teach any programming class or give a talk, I like to provide an initial outline of my “design philosophy” for development. I do this because it establishes a foundation for all of my thoughts and opinions. You may not realize it, but you have a design philosophy deep inside of you as well. What I want is for you to be conscious of it and begin to formalize it.
A design philosophy is important because it should be driving everything you do and every decision you make when writing code and structuring a project. It forces you to ask:
- Who is your audience?
- What are your priorities?
- When do you take exceptions to the rules?
- How do things work?
- Why you are making the decisions you make?
With that understanding you are capable of:
- Reasoning about tradeoffs and costs
- Determining when and why you do things
- Developing best practices and guidelines
- Smelling out good code from bad
- Appreciating others opinions
- Participating in healthy debates
- Refactoring your philosophy as your learn more
When you are conscious of your own design philosophy and have a vocabulary to talk about it, you can begin to answer questions like these with clarity and teach others effectively. Code reviews will become more productive for the entire team as well. But how do you begin to develop your design philosophy? It begins with an understanding of history, continues with learning the mechanics of a language, and is ever-evolving as you gain experience.
Everything we know and do today stems from discoveries and ideas of the past. To effectively develop your own design philosophy, you must take time to study history. Most of the philosophies I have are not original ideas or thoughts. They are borrowed and refactored from those who came before me. Without history, you can’t connect the dots or appreciate the decisions and tradeoffs that are being made today.
To get started, the following links from my training repo include a lists of documents and videos that I believe everyone should take the time to study. This is an amazing collection of thoughts and ideas from the past that should be considered when making your most important design decisions.
I’m continually updating these lists. If you have links to historical content that have helped shape your thoughts and opinions, please send them to me and I will add them to this collection.
Learning history is not enough however. You must also understand how things work. I call this learning mechanics. I believe learning the mechanics of a programming language is critical to developing an effective design philosophy for that language. This doesn’t necessarily mean learning internals, though sometimes this can help. What this means is understanding how language features function, the relationship between these features and the cost of these features.
As an example, in any programming language there is a relationship you must balance between solving problems in the concrete and decoupling the concrete from change. Concrete implementations provide the algorithm efficiencies and mechanical sympathies you need for performance, but decoupling provides the adaptability you need to handle change. Nothing is free and the cost of decoupling can result in some inefficiencies. So here is a question: when is it ok to take the cost of decoupling? Or maybe more importantly, when is it not? Understanding language mechanics and the problem you are trying to solve is critical for answering these questions.
Here are more questions. When should you share a value and when is it better to pass a copy? How do you determine what algorithms may be better suited for a particular problem? What are the costs and tradeoffs of using the different API’s that are available to you? Understanding language mechanics and the problem you are trying to solve is critical for answering these questions.
Experience and Prototyping
Finally, even with a solid understanding of history and mechanics you can’t validate your design philosophy without experience. As an example, I have spent several years thinking about logging and error handling and I have tried to develop a design philosophy that separates these two concerns. But I didn’t stop there. I have been implementing that philosophy in different projects to apply and prove my thoughts. Just recently I have concluded that it’s not practical to separate these two concerns. That separating these concerns will result in added complexity and prevent projects and teams from scaling up.
Coming to this conclusion has taken years of trying new things until I gained the right experience and found a solution that balances my different concerns. I plan to write a post about this among the other things that I have only touched upon in this post, but right now it’s more important to understand that your design philosophy needs to be tested, refactored and proven. It’s ok to be proven wrong because that is when the real learning begins.
My goal in this post, as in all my classes, is to get you to start thinking about what you are doing and more importantly, to have you begin to ask why. To have you begin to start formalizing your own design philosophy and use it to drive everything you do. To have you begin your journey of self discovery about what you believe, your best practices, guidelines and why you have come to these conclusions.
I’ve given you a road map to follow and I hope you take it. Read the links I shared to learn more about the history of computing, software development and design. Read the Go In Action book and the blog (which has over 70 posts to date) to learn more about Go’s language mechanics. Prototype as much as possible, try new things and trust your experience. Attempt to tie history, language mechanics and experience together in a comprehensive way, such that you can think about Go and appreciate the language like you never have before.