Tools Of The Best: Software Development Edition
By: Dmitrii Mokrushin
Tags: Programming, Design, Software Development, Development, Productivity

The day the product team is established, it is behind schedule and over its budget. Don Norman

How often do we encounter situations when, after learning useful information, we forget about it before we have time to put it into practice?

Below I have collected tips, thoughts and ideas based on the notes I have gathered over the past few years from the books, articles, courses I have studied.

The notes cover multiple areas, because fortunately over the past ten years I have been in different roles.

A significant part of the points is universal, but the focus is still on working on web projects from the beginning to the end.

Also, depending on the size of the projects, some items may not be adequate or applicable.

Since I gathered here the thoughts and ideas of some of the best people of the industry, I partly borrowed the title for the article from Tim Ferriss and his great book Tools Of Titans.

Contents

Ideas

Design:

* Base ideas

* Technical tips and tricks

* Accessibility

Architecture

Project work:

* Cognitive biases and mental models

* Communications

* Hiring

Development:

* Refactoring

Security

Ideas

Generate a lot of ideas at once. Don’t stop on one or two ideas.

Be creative, no matter what the circumstances. Don’t criticize ideas, either yours or someone else’s. Even crazy ideas, that often seem obviously wrong, may contain some kind of creative thought. Separate the stages of idea generation from the stages of idea evaluation. Do not give up ideas too early.

Limited time to generate ideas leads to hyper focusing and under these conditions it is easier to separate the phases of idea generation and evaluation, as there is no time to evaluate.

Sometimes you can add restrictions or remove existing ones to change the process.

Fears that interfere with creativity:

Fear seems stupid.

Fear of bad ideas.

Fear that you’re doing something the wrong way. Evaluating how people do their job should not be based on whether they do it your way, but on how effective their way is. If a person does something and succeeds, don’t try to retrain them.

Fear of the unknown.

Fear of not liking others doing something.

Fear that you’re not as good as you think.

Fear of impostor syndrome (you know less than others).

Design

This section is the most difficult for me, because I am not a professional designer, but at the same time it is one of the most useful, because this is where the value of this format manifests itself. When you can go through the points and become more productive in design discussions or applying ideas to work on your own projects.

Base Ideas

Remember that despite the rapid change in technology, human behavior is not changing so fast. And the way we acted 20 years ago is still relevant. See, for example, the latest report [how-people-read-online] (https://www.nngroup.com/articles/how-people-read-online/).

Use design instead of functional specifications, that only create the illusion of agreement. People read one text and understand it differently.

Pay no attention to the details at the beginning of the process. Move from big to small. Start with design and layout. They’re much easier to change than code. In design, start with the epicenter.

Affordances and anti-affordances of things must be obvious.

When affordances and anti-affordances are hard to detect, we must supplement the object with signifiers.

Related elements should be placed together. Put the controls and important information exactly where they are needed.

Feedback should be instant: a delay of even a tenth of a second can make a person nervous. If the delay lasts too long, people often give up and go do something else.

If there is too much feedback, it can annoy even more than if there is too little feedback. If we hear too many messages, we ignore them all or turn off everything. And that means we’re likely to miss really important messages.

Remove all error messages. Instead, provide users with help and guidance. Make sure that you can fix the problem right away with the tooltips.

Don’t expect people to save anything in short-term memory. Often, when something goes wrong, computer systems show a message with important information that disappears from the screen at the moment when a person wants to read it.

Avoid algorithms that start out the same way and then become different. The more experienced the employee is, the more likely he is to fall victim to such a miss. Whenever possible, the algorithms should be prescribed so they differ from each other from the beginning.

During the testing stage it is enough to interview 5 independent people/groups to get an adequate review. You can start with the other team members. Use open questions when testing.

Do not collect unnecessary information that you do not use now.

People often choose the first satisfactory option rather than looking for the best one. Our ideas about how things work are often wrong, so when we understand how something works, we do not look for an alternative solution, even if the current one is bad.

Steal like an artist. Use these sites for inspiration: (dribble, pinterest, pttrns).

Standardization is a fundamental principle of the desperate: when any other solution seems impossible, just design everything in the same way so, that people can learn something once and for all. For example:

A logo is in the upper left corner.

Familiar icons for the basket, videos, etc.

Ways to reduce errors:

Make the element, that the user is working with, more visible. Change the appearance of the object, make it more noticeable: increase it or change its color.

Add “undo” function for your operations.

Add reasonability checks.

Technical tips and tricks

Take into account normal, empty, and erroneous states in the design. Empty state makes the first impression, so it is very important.

Implement a light and dark theme.

Don’t set too strict restrictions on entering form fields. Especially in names. If possible, leave it to the user to determine how his name is spelled. And do not punish for not following fictitious rules and restrictions (formatting fields, etc.). Format the form automatically if possible.

Clickable things must be obvious. Do not use the same color for links or buttons and non-clickable headings. Make the difference between visiting and unvisiting links visible.

Use clear headings and subheadings to break down content into sections so that users can scan the content for information.

Do not let headers float between two blocks of text, it should be closer to its block.

Keep the paragraphs short. Shorten the texts by removing all insignificant words and sentences. Use a simple language.

Use lists. Highlight keywords. This allows the eye to focus on the most important information.

You probably don’t need a big picture on the main page. When people understand what the site is about, it annoys them with its size. Those who already know you don’t need it.

Light comes from the sky. Shadows are invaluable cues in telling the human brain what user interface elements we’re looking at.

Black and white first. Designing in grayscale before adding color simplifies the most complex element of visual design and forces you to focus on spacing and laying out elements.

Double your whitespace. To make UI that looks designed, add a lot of breathing room.

Use only good fonts.

Consider accessibility issues. In the current situation, a huge number of people who are not very familiar with computers have to use them in much larger volumes than before.

Accessibility

Increase the text to 200% and check what your product looks like.

Check the contrast. Browsers know how to tell you that there is not enough contrast. You can also enable high contrast mode in the operating system.

Add alt attributes.

Use the headers correctly. Do not jump over sizes.

Check labels for form fields.

Keep track of the availability of content from the keyboard.

You can see more details here:

Architecture

If you think good architecture is expensive, try bad architecture. Brian Foote and Joseph Yoder

Don’t make complicated architecture in new projects, as it’s not only long and expensive, but if your original idea and plans are not in demand, it’s difficult to adjust the idea and change the product quickly.

Develop a language based on the domain model for programmers’ communication with specialists.

Break the program into levels and isolate them from each other. Knowledge of the domain area should only be at one level. The GUI does not matter for business rules, so draw a line between them. The database does not matter for the GUI, so draw a boundary between them. Database is irrelevant for the business rules, so a boundary should be drawn between them.

Database is not a data model. A database is a part of the software. A database is a utility that provides access to data. From the architectural point of view this utility does not matter, it is a low-level detail — a mechanism.

User interface is a detail. Web is a user interface. That is, the Web is a detail. And as an architect, place details like this outside of the core business logic.

Make sure that there is low connectivity between modules, while within a module the connectivity can be higher than outside.

If during program development it turns out that the specialists do not understand some technical details from simplified schemes, or you do not understand the specialists’ descriptions, this is a signal that you are likely to have knowledge gaps in the domain area. And the architecture will fail if no changes are made.

Give suitable names to classes and methods. If you need to study its implementation to understand how an object works, then the sense of encapsulation is lost.

Take inspiration from math. Enter mathematical operations for objects. It can improve architecture and bring beauty to the code.

Stick to minimalism.

The Kent Beck’s strategy:

First make it work. You’ll be out of business if it doesn’t work.

Then make it right. Organize the code so you and others can understand and develop it when you need to change or understand something.

Finally, make it fast. Organize the code to achieve the desired performance.

Project Work

If you’re not worried, you need to worry. And if you’re worried, you don’t need to worry. Ray Dalio

Use the metrics to track product development. It is important to find a balance in the number of metrics. There should not be a lot of them to make it cheap and fast, but enough to see the problems. 3–4 metrics may be enough. If your metric is close to 100%, it may be worth changing. But don’t forget about communication, metrics are just numbers.

Keep in mind, that in the absence of data, people respond differently than when they have useless data. When there are no specific data, the a priori probability is used correctly; when there are useless data, the a priori probability is ignored.

In case of problems, use the “5 why” technique to understand the reason. You should ask “why” by constantly going deeper into the problem to find the true cause. Determine who is responsible for the result. If the result is poor, is it because of a lack of skills of a person or errors in the action plan?

You can manage a project with cost, time, quality and scale. Everybody should see and understand these variables and their relationship for good decision-making. The best way to manage variables is through scale. So, stick to your budget and time plans, but sacrifice scale. Cut off half of the ideas, then look at the rest and do it again. The lighter you are, the easier it is for you to change.

Start with small budgets — it focuses on the most important tasks, leads to ingenuity.

Travel light, it allows you to adapt quickly. Try not to get attached to vendors, proprietary formats, rigidly fixed road maps.

Do not solve non-existent problems. For each new task, try to find a way to implement it faster and cheaper. Keep it simple: tomorrow may never come or by the time it does, we will know the best way to solve the problem. Do not pay for flexibility that you do not need. YAGNI

Think about hiring testers. The cost of testers is much lower than that of developers.

Learn to say no to ideas. Consider the hidden cost of implementation.

Don’t limit people too much to processes, let them figure out how to solve the problem themselves. Let them make a mistake. You may even put cheap mistakes into the budget. It is important to analyze these mistakes so you do not repeat them too often. In most cases, people want to do a superb job, the manager is to help them do it. Mistakes are a natural part of the development process. Everyone has the right and responsibility to try to make sense of important things.

Avoid settings, they have a price to pay.

Move on with slight steps. Use an iterative process to improve the product. Test your solutions as soon as possible, do not copy until the final release. A prototype that doesn’t meet all the conditions can already be useful, for testing it with specialists or other teams.

Learn and make sure the team learns too. Although a person may eventually achieve a top level of skill in a certain field, challenges don’t become easier for him or her: the Olympic athlete finds his sport to be every bit as challenging as the novice does. Don’t expect your employees to define their own blind spots and compensate for them.

Play to win, not to lose. Don’t cover up with paperwork and plans to tell after that, it’s not your fault.

Take responsibility for your actions.

A 40-hour working week (±5 hours) allows you to be efficient and focused. Avoid extra working hours for two consecutive weeks.

Remember the balance of business and development. Everyone should be responsible for its own part and be available to the other side.

Plan in detail only the current release or iteration. The longer you delay planning, the more data you have and the more accurate you can predict the next steps.

Time is not a negative value, so time errors always accumulate on the right. This is asymmetry. That’s why we often make mistakes with time when planning.

Urgent issues are not more important than strategic ones. Sometimes less means more and we should often throw away unnecessary and insignificant information and focus on the key things. Remember the Pareto 20/80 principle.

Urgency is one of the major reasons for our distorted perception of the world. When we are under the pressure of urgency, we make bad analytical decisions. You always have to look and measure the data.

Issue log can be an excellent tool for the company: employees write down all the problems, the level of importance and responsibility. It helps to understand the reasons.

Pain + analysis = progress. Accept the stiffness that comes with the best motives.

One of the most important skills you need to develop is to ask for advice from people who are competent in areas where you are not strong.

Making assumptions and asking questions is not the same as criticizing, so do not take them that way.

Simultaneously evaluate the rate of change, the level at which the goal is and the relationship between them. If the situation improves, but the rate of change is low, then in adequate time the goal will not be achieved.

To make effective decisions, it is enough to understand most things on a general level.

Prioritize by assessing the importance of additional information in relation to the cost of postponing a decision.

First, do what you must do and then do what you like. Don’t get distracted by the bright trinkets and don’t forget about the mechanism behind your daily tasks.

There may not be enough time for the insignificant things, but it’s better than not having enough time for the important things. You can use the Eisenhower Matrix for prioritization by dividing tasks by urgency and importance.

Any task should be evaluated according to the probability of its successful completion and the level of its priority.

If you organized a meeting, manage the discussion.

Identify areas of personal responsibility for collective decision making. An impersonal “we” is an obvious cue that the person is trying to avoid recognizing a mistake.

Set up a bug tracker. Try to fix bugs before writing a new code.

Often you have to change the product, even if it already works well, to maintain user interest.

Remember that the idea that the new code will be better than the previous one may be quite absurd. The old code was already used, it was tested, and many errors were fixed. Remember that you throw away the knowledge that was in the project. Consider the primary reasons you want to rewrite the code and other solutions:

Architectural problems — it can be solved by careful refactoring verified by the entire team.

Performance problems — often 1% of changes can give 99% increase.

Ugly code — it can be solved by accepting formatting standards, setting up auto-formatting, git-hooks, etc.

When demonstrating your product, pixels are important, everyone will focus on the screen. Make it as beautiful as possible. But if you can display the unfinished parts in the interface so they look unfinished.

If you show a non-programmer a screen that looks 100% complete, he will think the whole product is almost ready.

If you show a non-programmer a screen that looks much worse than expected in the final version, he will think the whole product is much worse.

Cognitive biases and mental models

There are many cognitive biases as well as mental models. Many of them are described in Kahneman, Cialdini, etc. Knowing about their existence will help avoid some mistakes in decision making or improve product design. Below are just a few of those that I think are most relevant to the article.

Avoid the conjunctive events bias. We often overestimate the likelihood of conjunctive events-occurrences that must happen in conjunction with one another. The probability of a series of conjunctive events happening is lower than the probability of any individual event. So, we risk seriously underestimating the time, money, and effort required to achieve our goals.

Sunk Cost Fallacy — the tendency of people to irrationally follow through on an activity that is not meeting their expectations because of the time and/or money they have already spent on it. The sunk cost fallacy explains why people finish movies they aren’t enjoying, finish meals in restaurants even though they are full, hold on to investments that are underperforming and keep clothes in their closet that they’ve never worn.

Framing Effect — a cognitive bias, in which people react to a particular choice in different ways depending on how it is presented, as a loss or as a gain.

Communications

Do not hide until the last management decisions from the team, even if they are unpleasant for them.

Do not talk about the person anything that you didn’t dare to tell him in face.

Manage discussions:

Do not criticize during the speech and give everyone, for example, 2 minutes without interruption to speak.

Use active listening.

Identify a place in the queue for each person to ask questions after the speech. So, they will know that they will have the opportunity to ask questions and focus on the discussion.

Look for people who disagree with the proposals.

Involve the silent.

Hiring

Do not hire or hire as late as possible. Evaluate whether you may not do something or reduce functionality.

If you are hiring, you would prefer a smaller number of more talented people than a larger number of mediocre ones. This will reduce communication costs.

Groups of 3 to 5 people are the most effective in practice.

As Brooks’ law says, adding manpower to a late software project makes it later.

Think of an employee with what values, abilities and skills you’re looking for. The order of words matters here.

Assume that most people don’t change.

Prefer the generalists. Multi-task team members allow you to adapt more quickly to changing conditions.

All else being equal, choose a talented writer. These people usually think more clearly and communicate better.

Think of your teams as a sports manager: no one has all skills necessary for success, but everyone must be better at something. So, analyze where you fail more often and strengthen yourself. If you recruit people with the same strengths and weaknesses as the leader, it is more likely to lead to a negative result. The diversity of experience among candidates increases the chances that they will bring fresh ideas to the project.

Look for people who know how to get projects done.

During the interview, the candidate must show that he can write code.

Pay attention to actions, not words. One indicator could be the presence of open-source projects. There may be other indicators:

Passion for programming.

Interest in new languages and technologies because it may show interest in the profession.

Skills of working with low-level technologies.

An interview example:

Introduction. Your brief story about yourself, company, position. Here you can assure the candidate you care about the process of problem solving, not the actual answers.

Ask open questions about his/her previous experience. Sometimes go deeper into details. You can ask them to describe some complicated things in simple words to determine how real the candidate understands. Pay attention to the passion with which candidates talk about their experiences.

Give simple programming tasks. Here you should pay attention to the speed of task execution. It may be a good indicator of how a person will solve real tasks later.

Go on to more complicated things.

A section for questions from the candidate.

Development

I’m not a great programmer; I’m just a good programmer with great habits. Kent Beck

A codebase is a place where we live, and our goal is to make it livable for ourselves and for all the other people who live there. When we write code, our goal isn’t to finish it and move on, our goal is to make it sustainable, livable for the team that inhabits it.

You can think of yourself as a software writer, not a software engineer. “Writing” is a much more suitable metaphor for what we do most of the time than “engineering” is. Writing is about clarity and presenting information in a clear-to-follow manner so that anybody can understand it.

Write tests and keep the code as simple as possible. This way, you can reduce the cost of changes, avoid over-engineering and focus on current problems. Tests give you instant feedback.

Introduce the practice of updating dependencies every N days. It can be once a week or a couple of days a month.

Set up deployment processes so it happens painlessly for you. This will allow you to test the product and get feedback quickly. With negative feedback, you can easily roll back the changes.

Automate processes (recovery from failures, routine operations, etc.), as you are not the most reliable component of the system.

Set up linters that will perform automatic code checks, auto-formatting. This will simplify the work of all team members. At the code review stage, this will allow you to focus on business logic rather than coding style. Besides, you can set up code analyzers and get code quality checks or warnings about unsafe code. You may take a look at lefthook and prettier (or any other similar tools to your taste) and, depending on the language, these tools:

Use validators:

Refactoring

Tests make refactoring much easier.

Move in tiny steps and run the tests after each step.

Refactoring can be done immediately before adding an extra feature. This allows you to understand the code and make it better.

Always try to leave the code behind better than it was before you. Don’t make the code worse.

Don’t take too long to choose a name for a function/variable during refactoring. Choose the one that came to your mind and change it when you choose the best option.

In most cases, you may ignore performance during refactoring (for example, split one cycle into several). After refactoring, fix performance issues if they occur.

Refactoring is your job. Do not ask permission for it, but ask for advice. This is how you do your job, as a professional.

Always make sure the tests fall when they should fall.

Security

Pay attention to the storage of sensitive data in the repository. Do not store them open. Sometimes a framework can take care of this problem, for example [Ruby On Rails] (https://guides.rubyonrails.org/security.html#environmental-security) or [Phoenix] (https://hexdocs.pm/phoenix/deployment. html). Or you can choose a different approach, like [git-secret] (https://git-secret.io/).

Take care of the basic security of your servers. For example, if you have a few servers, you can see the tips in this article My first 10 minutes on a server and discussing on ycombinator. Either you can automate server deployment and create/find recipes for your favorite system: ansible, chef or something else.

Check that you have considered the most common security errors. For example, you can go through the list owasp.org/www-project-top-ten and check that:

You have taken care of the absence of injections in your code: SQL, NoSQL, etc.

Make sure that the authentication is not broken and works correctly.

Checked that there are no sensitive data and API in the public domain.

Made no mistakes in the security configuration, for example: changed the default passwords, correctly configured the firewall (especially relevant for those who use docker).

Take care of XSS.

Configured dependency update processes in case of vulnerabilities appear in them. For example, github sends reports on dependency vulnerabilities for different languages. This point can also be mitigated if you have implemented the practice of periodically updating dependencies described above.

Set up logging and monitoring of your systems

Conclusion

The key thing is to remember that projects and processes are different, so adapt the practices to your processes. You should not take the above as absolute truth. It is only an approach that allows you to miss fewer details, especially when interacting with things that you only occasionally encounter.

References