One of the most famous things I've ever created is RFC ZACH. This standard contains all of my beliefs on best practices for web development as well as programming and life in general. If you've worked with me for more than a few hours, I'll have probably mentioned something from this document. It started out as a joke because I am very opinionated, but it has evolved into something I can fully stand behind.
The best tool I've ever used for API documentation is Swagger. The Swagger team has written adapters for all of the popular languages a programmer can choose from to implement an API, which will generate a JSON file to define the request and response contracts. This file is then read by the Swagger UI to give API consumers an interactive way to comprehend the expectations of the API.
Swagger is a very mature tool, which takes a lot of the guess work out of generating API documentation. Because Swagger outputs a JSON file, it is possible to write APIs in different languages yet show their documentation on one page by combining the output JSON file. This is a pretty compelling feature that allows microservices to be written in whichever language suits the business requirements.
The disadvantage I've seen with Swagger is actually a disadvantage inherent to all API documentation tools I've ever used. It is extremely difficult from both an API producer and a consumer to clearly define all of the edge cases and exceptions an API may produce. It is nearly impossible even from a conceptual level to enumerate all of the exceptions a single API call may produce. The multitude of different methodologies and patterns that developers can use when defining the handling of the API call also makes it difficult to standardize on one way to communicate errors. This disadvantage is a problem in every API I've ever worked with, and seems to be a problem without a possible solution.
The purpose of an API should be to provide data for consumers of the API. They should be easy for consumers to call, which means clear and concise documentation, as well as consistent. The API contract is scared, so it should be easy for the language which implements the API to define the contract as well the software which enables the API should be able to enforce the contract.
A fundamental aspect of the API contract which should always be given consideration before finalizing is an agreement on the meta aspects of the API. The most important meta aspects are listed in order of their importance: performance, availability, notification of change. Performance is the most critical as a honest agreement on performance is critical gaining and keeping the trust of the stakeholders of consumers of the API. Availability has a large impact on cost as well as architecture, so having a clear understanding between the API designers and its consumers will make sure expectations are met.
Consistency in architecture closely mirrors consistency in life, and plays a large part in maintainability. A consistent architecture is one where developers can be trained in understanding patterns, and see the implementation of those patterns reflected in the code. It is best if the architecture is consistent with best practices and designs outside of the architecture itself which may allow new developers to leverage existing knowledge. It should be required though that the architecture is consistent within itself.
Maintaining consistency over time is extremely difficult. New code can be introduced which inadvertantly or maliciously violates the architecture's consistency. This frequently happens when third party libraries are introduced without the architecture team's approval. Thus, at a bare minimum, there should be mechanisms by which the architecture team allows developers to introduce new patterns, concepts, or libraries into the architecture. This is frequently in the form of a Technical Advisory Board. This ensures that the architects can approve or reject changes from a higher vantage point than the developers proposing the changes.
To be clear, this opinion deals with exceptions in an architecture's design rather than exceptions that are handled by a try/catch statement. This kind of exception is very close to the life exception.
Every exception to an architecture has a cost. New developers have to remember both the patterns as well as times when to break the patterns. This flexability can make developers nervous on if they're breaking the pattern correctly, or if they're taking it too far. What's more often the case is that developers forget the exceptions to the rules, and incorrectly implement the pattern. Or they forget what they're doing is the exception, and proceed to use the exception even when it is not applicable.
Exceptions should really only be allowed when they serve consistency at a higher level. The higher up the exception is, the more costly it is. Great discretion should be taken when allowing exceptions in an architecture. If possible, exceptions should be avoided at all costs.
Most commonly, this phrase is used as a justification when an architect does not want to make a clear decision. See exceptions.
Almost everything we do in architecture has its roots in the physical world. This is because humans are the ones which write software, and existence in the physical world is an essential aspect of the human condition. This parallelism does limit the level of abstraction that can be maintainably architected into software, so the limitation should be accounted for.
The most important parallel idea between architecture and life is that of communication. Source code is a communication with two intended recipients: a machine to interpret the code, and a developer who needs to read and understand the intent of the code. A violation of this requirement for clear communication is possible as long as it is the most optimal way to achieve the intended goals. Outside of that exception, architects should define an architecture that allows for effective communication with both the machine interpreter as well as the human developer interpreter.
Maintainability in software architecture is a function of several different aspects of the software development lifecycle. If software is architected in such a way that new features can be added or removed from it while minimizing the impact to code outside of the feature in question, then it has a high degree of maintainability. If the software can given to an operations team for long term support, then it is maintainable. Maintainability has its roots in sustainability as a highly maintainable architecture is one that can be sustained and grown over a long period of time.
There are choices, however, that can be made for the sake of maintainability which actually make the software less maintainable. Often these choices come in the form of scope creep or a desire to make the code more generic. By increasing the degree that code is generic, its degree of abstraction also increases, which may make the software harder to comprehend and maintain. I have also seen projects which focus on being generic for maintainability's sake so much so that nothing actually ever gets accomplished. Abstraction with a goal in mind of implemention is maintainable; abstraction for the sake of abstraction is an anti-pattern.
A balance then needs to be struck between a desire to fit every purpose, which is unmaintainable, and filling the needs at hand, which can be so specific that it hinders the ability to reuse code. A clear goal should be set by the architects which constrains the upper bounds of support for a project. This goal should be high enough to allow for growth, yet placed low enough that it will become clear when the current architecture does not meet future needs and becomes unmaintainable. I can't find the original citation, but I remember reading something from IBM that they design their systems to allow for a logarithmic increase before it needs to be architected again. Thus, I believe code that has been architected to allow for 10x its initial constraints is a good goal to set, and will allow for maintainability.
Magic in architecture should be avoided. What I mean by magic is inexplicable features which violate the expectations communicated to developers. AngularJS is a big proponent of magic architecture, which leads developers to spend more time trying to understanding AngularJS's magic rather than trying to understand JavaScript as a language.
Magic violates our internal notion of consistency with expectations. Many developers assigned to supporting legacy applications have found that they either need to quickly learn the magic fixes they can perform on an application which make no logic sense or they can hand in their two week's notice to find another job. Jobs have been lost over magic. Fortunes have been made and lost over magic. If creating a simple and maintainable architecture is important, then keeping as much magic out of it as necessary should be a high goal.
There should be no exceptions made in the security model of the architecture. The cost of a security mistake far outweighs any benefit from the exception.
There are some subtle nuances of simplicity in architecture that make it slightly different than simplicity in life. Architectural simplicity is less of a destination and more of a process. Architects should focus on building a complex system by composing it of parts whose complexity decreases the farther it is from the center of the application.
Truly effective architects are the ones who can take a problem, boil it down to the smallest pieces necessary, and produce an answer which both solves the original problem as well as offers the maximum sane reuse.
There are generally two outcomes when an architecture is incorrect. An under-architected system is so tightly defined that new features are too broad to be added, and misses solving the original problem. An over-architected system is so generic that new features are so specific that they are expensive to add, and also misses solving the original problem. Defining an architecture that allows features to grow in an organic and sustainable way while still solving the original problem at hand is the sweet spot for architectural simplicity.
I often use the concept of a car to describe simple architecture. Designers of cars compose a very complex system out of small pieces that by themselves are easy to understand while still working together through a well defined interface to solve the problem of locomotion. An over-architected car would have focused on the smallest possible pieces of the car, which can be either molecules or atoms or quarks, and mired the work that builders of the car needed to do so much that nothing was produced. An under-architected car would not have focused on reusable pieces with well defined intefaces to the point that responsibilities would have become blurred, and one-off and unreusable pieces would have composed an object that possibly resembled a car.
JavaScript exists to breathe life into static pages in a way that provides positive value to end users. It allows developers to get so close to the end user's experience that care should be taken to ensure quality code is delivered. A concerted effort should be taken to ensure the negative aspects of JavaScript are not abused, and that maintainable code is the output of all work.
The current state of JavaScript is as exciting as it is terrifying. JavaScript is finally at a point where it can be considered a viable option for enterprise-level UI applications. It is receiving a tremendous amount of attention from developers across the world. Thousands of people are contributing to Node packages every single day, and working to drive the language forward in new ways I never imagined when I started working with JavaScript in 2005.
There are two primary facets of JavaScript in 2016 that terrify me: JavaScript developers, and JavaScript projects.
JavaScript developers as commonly known for pieceing together discrete plugins and copy/paste StackOverflow code to create web applications that are so fragile they make all of us seem like fools. Dispite what job titles in Silicon Valley may indicate, these people are not "engineers" or really even "developers." They are hackers in every sense of the word. They are not concerned with things like consistency or maintainability. Only with getting a feature out the door fast enough to apease the unrealistic expectations of their manager. The only way to make these hackers into developers is to do something which does not scale: give them your time, and teach them what it really means to be a developer.
JavaScript projects that make me worry are ones that commonly come out of startups, but are also in the category of weekend projets. JavaScript has such a low barrier to entry that it seems like anyone can take an idea, and get a working prototype within a few hours. This low barrier gives JavaScript a lot of its initial appeal, but comes at a price with developers thinking they are business people and trying to create products in the form of a startup. Every time you see a new NPM package rise to the top of Hacker News, ask yourself: what is the name of one human being whose life is improved by this code? In nine times out of 10, the code was written for the sake of being written, and honestly does nothing to better the human condition. The only way to fix this is to also do something that does not scale: give them your time, and show them that reading a book or volunteering is a better way to spend an afternoon than creating the next NodeJS task runner which no one will ever use.
Being able to effectively communicate the ways and means to accomplish a goal, as well as the goal itself, requires the communicator to have a clear vision of every step of the process. Breaking down all of the pieces to a puzzle is a difficult task for anyone to accomplish. Clarity is expected from both leaders as well as those being lead.
Consistency plays a large part in clarity. People have an innate ability to figure out when communication has become inconsistent, which leads to questions on the clarity behind the communication. A well thought out plan that is clear in the mind of the communictor tends to be consistent throughout. There are times when the person receiving the communication does not have enough of a clear vision to see the consistency in the message. If this is the case, then the person doing the communicating should account for it, and present the message in a way that takes into account the biases and perceptions of the person receiving the communication.
The ability to effectively communicate greatly determines a person's success both in terms of career success as well as personal success. Communication is a skill that is worth the effort to learn and perfect.
Communication plays an essential role in every aspect of life. The clearer two parties are in their communication, the less friction between them there will be. The principles of managing expectations and following through with commitments rely heavily on the ability to effectively communicate.
Life itself is very inconsistent. There are so many variables that go into every little action a person takes that we've trained our brains to find patterns and consistencies. Thus, there is a subconscious affinity to people or things which are consistent, and likewise a subconsious adversion to people or things which are inconsistent.
Every exception to a rule has a cost associated with it. Sometimes the cost is mental in remembering the exception. Sometimes the cost is material in time spent implementing exceptions. Whatever the case, exceptions should only be allowed at a micro level if they allow goals at a macro level to be more easily achieved. Thus, inconsistency is allowed at a lower level if it achieves a consistecy at a greater level. It is extremely rare that inconsistency at the macro level is correct.
There are several, mostly incorrect, ways to view leadership. The only view I've found I can be happy with is the notion of a leader as a servant to all. Good leaders place those they are responsible for above themselves. They are servants to those that allow them to lead, and in doing so are able to grasp the full responsibility they have been given.
One of my favorite books as a kid was The Giving Tree. The story goes that a tree so loved a little boy that it slowly gave it everything it had until nothing was left of the tree but an old stump. The boy returned to the tree as an old man, and the tree begged for forgiveness as it had nothing else to give the boy. The boy said that as an old man he didn't need much, just a quiet place to sit and rest. The stump, haven given away everything it had, felt proud that it could straighten up and meet even this last need of the boy.
This is what I aspire to be.
This quote comes pretty close to my feelings on simplicity. It is a quote probably by Albert Einstein:
Everything should be as simple as it can be, but not simpler.
There is a constant struggle between effectiveness and simplicity. If things are made too simple, important nuance is often overlooked which leads to an incorrect understanding. If things are made too complex, it becomes easy to be mired in the unnecessary and miss accomplishing anything. A balance between those two important forces is necessary to achieve anything as well as gain an understanding of the world which is as correct as possible given the nature of our biased perception.
There are many different ways that people approach achieving a goal. Some people put all of their cognitive energy and time into achiving it. This single-mindedness can often lead to burn out, or inadvertantly negatively affect relationships with those that they care about. Though this style may lead to the goal being achieved, the consequences of this tunnel vision can be disasterous.
There are a few possible causes for people beliving that going all-in is the best way to achieve a goal. The most probable one I've found is that the person in question is thinking in a way that is too short term. They are so focused on achieving the goal that they forget to think through what will happen before, during, and after the goal is met. If any one of these stages is not accounted for in the plan to achieve the goal, negative consequences are bound to follow.
Thinking about achieving long term goals leads to thoughts on sustainability. A sustainable approach is one that accounts for every phase of achieving the goal in such a way that negative consequences are minimized while still accomplishing the original goal at hand.
Focusing on sustainability means recognizing that work must be done to achieve the goal. Thus, knowing a person's limits is paramount to determining how best to schedule the work. There are some people who enjoy quick bursts of activity followed by a cooling down period to reflect and regroup. There are others who would rather make slow and steady progress every day. Figuring out what works best on a personal level is the key to finding a sustainable way to achieve long term goals.
Wealth is created when the desires of a person are met or exceeded. The degree of wealth created is proportional to quality of how well the subjective desires have been met. Sometimes these desires are in the form of helping them solve a problem. Other times possessing an object is desireable.
Wealth is not money. Money can be a side-effect of wealth. There are other side-effects of wealth, such as the smile of a child that is happy, which are more difficult to quantify yet are still an indication that wealth has been created.
Creating wealth for its side effects is a hollow goal. Altruism it not possible, though it is the closest we can come through our subjective experiences to a correct goal of adding wealth.
JavaScript IDEs are difficult to write and use because of the flexable nature of the language through prototypal inheritance. The menagerie of different ways to create a JavaScript UI application also makes it hard to write one IDE to solve all use cases. The best tool I've seen for UI development is actually Node.js itself.
There is nothing inherent to Node.js itself that allows it to be a bona fide UI development tool. Writing JavaScript to help write JavaScript is novel, true, though it is not the reason why Node.js works so well for UI development. Instead, it is the Node.js UI community that makes such a unique tool. There are thousands of UI developers that have latched on to the Node.js platform, and have chosen it as the de facto platform to write tools that make developing quality UI code easier. This phenomenon could have happened in any IDE, even those beguiled IDEs like Eclipse, if the community around those IDEs would have put more time into making tools for UI development a priority.
Node.js itself allows for tools to be written on top of it which interact with the JavaScript source code at a system level. Task runners allow developers to hook into scheduling and orchestration of builds. Code quality tools at a command line level allow continuous integration software to hook into metrics they have never had access to until now. Allowing for all of these features through a command line interface makes Node.js the backbone for UI development.
Defining and enforcing the API contract is fundamental to an API's purpose. Every other server-side language mentioned in this RFC outside of Node.js has mature and stable API frameworks that to an extent easily allow developers to define the model requirements, and throw exceptions when the consumer of the API violates the request contract. To the best of my knowledge, this level of API contract definition and enforcement is precariously missing from any Node.js API framework.
JavaScript's purpose is to allow developers to transform static pages into a dynamic experience that is positive for end users. It was designed to allow for a flexable definition of code through mechanisms like prototypal inheritance and loose typing. This flexability comes with a cost in terms of performance. Boxing and unboxing variables along with other implementation details of being an interpreted language mean it is not an optimal choice where performance is concerned. Thus, defining and meeting SLAs is difficult due to the performance of the language itself.
Abbreviations are a malicious form of inconsistent communication. They require the speaker and the listener to have acquired the same parlance outside of the conversation. Thus, there is hte potential that either the listener or a third party listening in does not understand the speaker. The cost of misunderstanding the communication is prohibitively higher than any benefit that comes with using abbreviations.
Abbreviations should only be used when the communication between two entites is not critical, or the abbreviation is so common place that the probability of misinterpretation is virtually nothing.
Things said before the "but" tend to matter very little in comparison with what is said after. Avoid trivializing the audience by limiting the use of "but" to situations where it makes more stylistic or linguistic sense than alternatives.
Using negatives like "not" and "never" should be avoided to ensure effective communication. People tend to prefer communication centered around positive words. Injecting "not" and "never" gives the communication a negative connotation, which makes the message trying to be communicated less effectively received.
Words like "not" and "never" also hinder the clarity of the communication. This category of words forces the person receiving the message to flip around pieces of the message from their pure, positive meaning. This process is further complicated when multiple negatives are included in the same sentence.
The only time that "not" is allowed is in situations where omitting the word decreases the clarity of the message.
Politics exists in the realm of the physical. Religion is the domain of the spiritual. Thus, these two important facts of the human condition are often incompatible, and should intersect as little as possible.
For me, religion defines the behaviors and views I believe are consistent with God's will. Politics is the scope of how humans should interact with each other, and how they should be governed. The goals of these two are disjoint, which is why I have a hard time believing that political laws should be a subset of religious beliefs.
One important place of intersection which I do believe is correct is in the spiritual responsibility that the leaders of the government have to those whom they govern. This is different than the notion of spiritual authority that some believe leaders have. I am more in the camp of leaders as servants than leaders as bosses. Leaders have a fundamental responsibility to place those that gave them the power to lead above themselves. If that mandate is not being met, the people being lead have a responsibility to either force the leader to fulfill it or elect another leader.
In C.S. Lewis's book The Abolition of Man, he extends the idea of rejecting morality on rational grounds to its ultimate conclusion of a race of humans who are truly not human at all. While I agree with most of what Lewis says, I believe morality itself is the physical expression of a spiritual yearning to be in line with God's will. Attempting to remove this desire can make us inhuman, though removing morality itself is only part of thestory.
The analogy of men (and women) without chests is one that characterizes humanity more than Lewis may have realized. To be human means walking a line between the physical and the spiritual. We are creatures that exist in both realms at once. We can only experience the physical, which means we unable to experience the other half of our being. This spiritual hole in our collective chests is the primary motivator for almost all of our actions.
The natural implication of the hole is the desire to fill the hole. Every person knows they are incomplete in some manner, and how they choose to try and complete themselves determines a good majority of their actions. Filling the hole through physical means may make the hole feel smaller on a temporary basis; however, the hole is a spiritual hole, so filling it through the physical will be ineffectual.
The way I choose to fill the hole in my chest is by creating wealth in a sustainable manner by maximizing the talents that were given to me through grace. Those talents are leadership, programming, and teaching.
All businesses are able to exist because they receive money to create wealth by solving problems. The degree of success a company has is in large part to how effectively it solves the problem as well as the severity of the problem and how much people are willing to pay for them to fix it.
Companies that have matured know this subconsciously. Startups, however, seem to have a very difficult time keeping this in mind. They often come up with solutions to problems that no one has, or a problem which is benign enough that people can ignore it without paying money for someone to fix it.
Thus, a startup should exist to solve one very painful problem which people will pay money for the startup to solve. Startups should focus on being extremely good at solving just one particular problem. It should be a problem that the founders themselves have. This ensures the startup is able to clearly focus on solving the problem before paying customers can help them focus more acutely.