Pay the technical debt as soon as possible.
- If a path must be chosen to fix a bug, pick the correct way, not the quick way because quick way eventually can't be handled and correct way must be tried in a more complex system.
Learn functional programming.
- Referential transparency, a function always returns the same output with same inputs, is one of the first cornerstone of writing rogue software with fewer bugs what we all want. Functional programming is the best way to get it by heart.
- In each step, ask yourselves if this is what the user want and the way user will interact.
Automate coding standard.
- If you want everybody really to use it because coding standards don't bring anything and deadlines are strict but in the long run, it is deadly important especially for maintenance. For example, at MVHub, we are using perltidy as a test.
Beauty is in Simplicity
- Each part of the code must be kept with simple functionalities and best way to get used to it is reading beautiful-simple code.
Refactor, no worse than before or better in some way
- Move small and validate every reason behind refactoring while leaving the tests pass.
Reuse caring about context.
- Reusing makes the code shorter and simpler, so beautiful but even if two modules do the same thing and are seemed to work exactly same, context makes the difference and they can-should evolve differently so be sure first.
The Boy Scout Rule.
- Try to make each check in better in some way.
Believe that your code is broken.
- It is hard to believe but please first check your code and be sure before going for dependencies.
Don't invent the wheel but choose the tools carefully.
- License, vendor dependency, update frequency and configuration are just some of the points to think about. Moreover, try to isolate main application from external tools via interfaces.
Code in the domain.
- Use domain specific names which makes easier for newcomers to adopt.
Code is design.
- Programming is a design process and may still be the only thing that is completely hand made in the world.
Code layout matters.
- Programmers spend much more time while searching rather than typing so defining default locations for similar parts makes easier to locate what to edit. Using meaningful names makes it further easier. Last but not least, if code is compact, then there is smaller place to search and read, and it is like a poet since each particle in the code is required and serves a function.
Do code reviews.
- Main goal of review process must be sharing knowledge, not correcting the mistakes. Try to involve whole team, experts can bring their experience to identify error faster and newbies can bring their fresh college knowledge to innovate a new way of doing. Meeting should be going on informal and no hierarchy should be given to programmers and task must even be shared in a round-robin fashion.
The correctness of the code starts small.
- No goto and modifiable globals (each variable should have the smallest scope possible). Prefer immutable object if it applies. Functions should be short with maximum 4 parameters if there are more than 4 parameters, group them to increase relevance and consistency. Try to keep the interfaces to external parts as narrow as possible.
Don't forget comments.
- Header comments should explain what your code does for one who wants to use your code and inline comments should be enough for one who is trying to fix a bug or build a feature.
However knowing what not to write is also important.
- Wrong comments or comments that add no value (not explain anything new or cause misconceptions) shouldn't be there because when they get in, they are there more than they should be. Comments should be about what the code can't explain. By the way, don't forget the better code is, the less comment is needed since the code itself may explain everything.
Don't become a dinosaur and always learn something new.
- Technology is advancing very fast so keep learning a bit every day without forgetting the life passing outside.
API design is difficult and requires some thinking.
- API should be implemented while caring about the user of the API, not the developer. Compare run to walk(true) which one is more intuitive and encourage people to use.
Deployment should be tested and refactored as the code is done.
- What is seen by the user is production environment so it should be well tested. In the local, developer has the full control but some assumption may not be valid any more in the production environment. Frequent checks are needed to be sure that nothing is broken. Easy deployment is trivial, no accuse just do it, complex deployment then iterate and learn so do it often. Watermelon gets bigger by lying down, a child learns by making mistakes. -a Turkish saying
Where do exceptions come from?
- Domain specific and technical exceptions should be distinguished, this gives clarity. Otherwise, end part doesn't know the actual source of the error. Actually domain specific exception may be completely a valid path.
- Do deliberate practice, repeat the process while slightly increasing the difficulty of the process each time to challenge ourselves. One motivation may be that the one that gave the most time becomes the best at the end, not the one that intrinsically has the skill at the start.
Domain specific languages.
- Each area has its own language and using it makes easier most of the things, dream about explaining B-tree to someone that knows nothing about trees other than green plants. Do empathy and try to use the language of the users.
Don't be afraid to break things.
- Not to try for the better due to scaring if something goes wrong causes much more at the long run and breaking things is a sign of improvement, doing better and keep learning.
// TERRIBLE HORRIBLE NO GOOD VERY BAD HACK
- Don't write anything that you wouldn't write in public for comments, logs, test data, error messages, etc.
Be more serious about errors.
- Don't assume that everything will go allright and don't ignore the errors and try to handle as soon as possible. There is a good example, don't assume malloc will always new memory but if it doesn't, then we have much more serious problem.
- Always be on the track to learn new ones because each one makes easier to use the other in the limits.
- Sometimes we tend to use try blocks to prevent our code from crashing but then we don't report exceptions as they should be, see 26. Our code seems to run forever and crashes somewhere else, not in our well escaped part, then what went wrong is secret so put some robust logging mechanism.
Magic, there is no such thing.
- People assume things to be done by magic due to their nature unless they are really into it but there will always be some time you needed to know so try to be generalist to know a bit about everything. TODO: learn JVM memory handling or class loading, etc.
Don't repeat yourself (DRY).
- Every piece of knowledge must have a single, unambigous, authoritative representation within a system. It qualitatively decreases the number of the lines so code base becomes cleaner and modifications take part between relevant parts that makes understanding easier since each module is self-contained logical unit.
Don't touch that code!
- First reaction is let me fix but in each fix, pipeline should start from local development and then code should be pushed to staging server to do automated testing and everything goes well, it will be packaged for production. Production server should never be the place to fix the bug.
- It is very useful construct if it is done correctly. Very short or long classes are just one sign of inproper usage.
- In computer systems, they are just approximation of real numbers so there are intrinsically roundoff errors, be aware of it and code accordingly, i.e. don't expect better resolution than the number system can provide. For financial applications, prefer not to use them.
Open source where the real fulfillment is.
- Be able to work what we really want to work.
- See different styles of solutions that matter for you.
- Engage in with our brilliant ideas.
- Meet people that have the same passion.
- Add a real-value to the lives of millions of people.
- Best way may be writing test code which teaches much more than expected.
Golden rule of API design
- API design is difficult because all possible changes should be foreseen.
- Being able to write tests for API is important to see overall goodness but golden rule is writing tests for the code that makes API calls to make empathy with our users.
- A guru is a smart person with relentless curiosity but nothing more than that, so not capable of magic, i.e. suggesting a wise solution without being provided context.
Prepare, effect, observe, reflect and change.
- Software development is a bit different than other sectors. We should always educate ourselves, then apply new techniques and experiment the results and accordingly change the plan to take necessary steps in which requires some off times to observe the environment, i.e. meeting fellows, reading blogs, books, and HN, trying some _Hello World_s. Workload around 30 hours/week seems reasonable.
Bug tracker - how to
- Bug tracker is the one of main components that provides a pace for the project improvement so required parts of a bug:
- How to reproduce it? and how often it happens?
- What you expected?
- What you get in real?
- Try to put as much as context about the environment in which bug is seen and prevent yourself from using personal beliefs.
- Having a bug template saves much time for submit, search, etc.
Less is more. (If you are thinking about pagers, you are geeker than you see yourself, I think)
- What we are currently working on may be YAGNI (that is, You Aren't Gonna Need It) because
- Writing code is fun and we think we add value if we write more.
- May be needed in the future so let's write now since we come back later our knowledge about context/code would be less than the instant now.
- Some optional/extra features are found and are implemented before asking the client to reduce turnaround time.
- Requirements weren't obvious and developer became an inventer.
- must be very easy, at most a script to run after download.
- Well explained tutorial and the explanation of some foreseen errors also affect my thoughts to install your software.
- Unresponsive software is unbearable since it makes us to feel ourselves wasting time and being unproductive. Main solution to increase performance is tuning data structures and algorithms which is far less important than expected in multi-tier application. Dominating factor is communication between components.
- Requesting what exactly is needed such as lazy loading
- Parallelizing calls to reduce latency to the longest call duration
- Minimizing calls by means of caching
Warnings and clean build
- Repetition of rule 1-Technical debt, remove the source of the warning ASAP to reduce noise for better Signal-to-Noise-Ratio(SNR) which makes development healthier.
- For sure, IDE is invented to improve programmer's productivity but it also makes a lot of things under the hood so learn to use command utilies what really is going on.
At least two different paradigm programming language
- Different paradigms also improve our main language so always be on the track to learn new ones.
- I am following Seven Languages in Seven Weeks and rigorously recommended.
- Most IDEs are easy to get the basics and to be productive but when programmers are able to go further with this set, they think that is enough. However, we shouldn't stop there and try to learn the secrets of the IDE because when small context switches due to not knowing shortcuts add up, a lot of time in which we can be very productive by IDE shortcuts just becomes a waste. Learn your craft.
Know your limits
- Be aware of Big-O notation which is calculated on an hypothetical machine so execution on real machine differs in terms of efficient usage of caches such as performance difference(2:1) between Binary search(log(n)) and search of van Emde Boas tree(log(n)).
Know your next commit
- Divide overall picture into small tasks that can be completed within a couple of hours so that we will be more focused and aware if we are on the track of what is planned beforehand.
- When data come to scene, rely on databases because
- Easy accessible and free (Floss)
- Better than algorithm tuning (query optimization)
- Even no need to know about SQL (ActiveRecord of RoR)
- Better usage of resources (compare the map collection of a programming language to index- based DBMS)
- Programmer must be fluent in the language of computers since software is created in this way (so far) but that's not enough because software systems of today are very complex so programmers must talk to peers, end users and a lot of people from different backgrounds between them and without speaking their language communication/understanding each other isn't possible. Moreover, the more fluent programmer is in his mother tongue, the better he can explain/abstract the problems that is programming exactly is about. Btw, “Actually a person does not really understand something until after teaching it to a computer, express it as an algorithm." Donald Knuth
Learn to estimate
- Programmers are the most opportunist people of the world but reasonable estimate which comes from well-defined goal and quality specification of the goal, is important for trust and allocation of resources (time, budget, technology).
Extreme feedback device(XFD)
- Define a lively object to notify the status of the project such as a lamp in the office with different colors, green means everything is going on as planned, red means emergency steps must be taken, etc.
- Source -> Compilation -> Linking -> Execution
- Linking step is usually seen as magic but linker isn't a magical program, finds all references and connect them to their definitions. Three related problems are defined more than once(multiple declaration), unresolved symbol(declaration without definition) and huge executable(linking libraries more than enough or dynamic linking due to symbol table).
Interim solutions stay there forever
- An interim solution is some code that already solved the problem and met with its users but doesn't follow the convention and quality standards. However, it is in production and it works so nobody wants to rework on it because it is just a mess. As a result, it will be an inseparable part of code base. Only exception is emergence of a super hero with better solution which is (day)dreaming.
Develop intuitive interfaces
- Interfaces come up in very different levels such as private properties of a class, parameter specification of a service or interaction of end users.
- +Boost productivity
- +Pleasure to use
- -Prevented to be used
- -Source of errors
- Therefore, two main parts of good interfaces are
- Easy to use correctly (Use it before developed, test-driven development(TDD))
- Hard to use incorrectly (Search for the source of the incorrect usage and remove it)
Make the invisible more visible
- Most parts of the software development are implicitly invisible since Moore's law still holds and relatively complexity dramatically increases, there are a lot of details to care about. Last but not least software development is still done by hand and may be the only thing which is handmade today. The more visible progress is, the less errors will be encountered (at least earlier) so the ways to pull the pile:
- Writing tests
- Running tests
- Collaboration - bulletin board (redmine, trello)
- Agile development
Message passing leads to better scalability
- Due to multiple cores, paralelization is the ultimate way of increasing performance and message passing is the easiest method of all because it eliminates implicit problems such as deadlock, livelock, race conditions which deteoriate performance.
A message to the future
- Yields a smaller, more readable and less fragile code base by delegating execution to the relevant object itself so be aware of it compared to using if-else-then block.
Testers are our friends
- Generally testers are seen as enemies since they find our bugs but bugs found by testers are better than found by end-users. Testers are actually people who makes us to seem perfect at the end so we should learn being friends with and utilizing them as much as possible.
- Keep environment related information separated from the code to decrease possible errors and also version it to keep track of changes and easily reverting.
The only truth comes from the code
- Every document can lack of some details and may even be errrornous. Comments are no exception. The ugly truth can only be seen by the source code so make it to talk to you and other programmers by
- Good naming
- Structure with respect to functionality
- Automated tests
- Incrementally refactor it to apply new simpler methods, legacy code is actually a succesful code because it is there and solves the problem for a long time but may lack incremental improvement that's why it's not beautiful.
Refactor the Build
- Since build script is usually written in another language than source code and it isn't really seemed as code, it is neglected. However, source code without build process is useless and decisions taken by build step can have deep effect on the development and can also decrease the costs so it should be a part of the development. Moreover, automated build enables new developers to easily engage in development and prevents it works for me conversations.
- Increase load sharing and prevent some members of the team overwhelmed by the load.
- Solve the problem effectively by discussing the various solutions with the pair and picking the best.
- Integrate smoothly by rotating the pair since your pair might be a part of the coders of the method you are trying to call.
- Mitigate interruptions by while one of the pairs is out, the other is still working.
- Bring new/less skilled developers to the front quickly by sharing knowledge.
- Multiple software failures are seen due to some parameter mismatches. Domain-specific types may prevent them by expressing terms in terms of domain, not primitives that can be confused, making code more testable and reusable. Moreover, domain-specific types also get some help from compiler to hunt the errors.
- Keeps her/himself up-to-date by continous reading and learning.
- Tries to leave nothing for testers and her/his attitude is always thoroughly testing the code before passing it to pipeline.
- A good team player by learning from, teaching to, covering and helping each other.
- Long bug lists aren't tolerated since it is seen as sloppy.
- He doesn't make a mess because he keeps the code clean, follows standards and schedule.
Put everything under version control
- There is no need to explain benefits of it, they are already in the wild. The only thing I can say is that after I realized benefits, I don't even write notes to myself without it and now I regret not using it in the start of the university since I have lost everything I have written from that times.
Take a break
- When you have a nasty problem, take a break and when you are ready, come back. I have already adapted this advice with jogging or tv series.
- Read code instead of reading programming books. Your old code helps you to see how much you improved and to detect problematic parts you weren't aware while writing. Someone else's code also helps in two ways depends on its quality. If it is better, try to see why it is better such as convention, design patterns. Otherwise, think about why it is seemed bad to you and what can be done to overcome.
Read the humanities
- People write software for people with people so people should learn how people think where one way is examples from history, prototypes.
Reinvent the wheel often
- The more complex systems get, the more software is used as black boxes. Therefore, we aren't aware of the internals. Using existing products is important in terms of being pragmatic programmer but implementing it from stratch is the ultimate learning.
Overcome the temptation of using singleton
- Don't use singleton since in short, it hinders testing and maintainability by liking global keyword.
Performance passes through code bombs
- Performance tuning mostly requires refactoring of some dirty code (bombs) which can be detected by high fan-out (the number of classes referenced by interested class) and low fan-in (the number of class reference to interested class).
Reduce to only what is needed
- Keep only what is needed to get the work done and remove anything else as soon as possible because that's way simplicity is given birth.
Single Responsibility Principle
- Putting multiple things that can be changed by different reasons into same logical block is an example of bad design so keep it to focus only one tast for robustness, maintainability and deployment.
Start with YES
- When clients and managers want new things to implement from us, we tend to finish the speech by saying no but instead try to question the reason behind this request.
- Automate your repetitive tasks:
- Don't stop for only tests
- IDEs are't enough since their automation requires a lot of configuration/repetitive steps
- There is no need for fantastic tools, bash or PowerShell is sufficient.
- Automation is a bicycle skill that repays itself easily.
Take advantage of code analysis tools
- Testing is just one of way of the catching bugs and errors. Now, code analysis tools come free such as Splint for C, Pylint for Python and can be very helpful so why don't you incorporate them in to development?
Moral of Tests
- Tests must test the intended the feature, not what the code exactly does.
Being accurate isn't enough
- Tests must be defined precisely. Otherwise, tests could easily raise green flag for buggy code.
For example sorting following list:
If we define the test to provide an increasing order and same number of elements as the original list, then following result is perfectly valid:
But, this isn't what is intended.
Test while you sleep
- CPU cycles are wasted at nights or weekends which are perfect times to run tests and quality assurance. What about using these free times but we need to automate our tests.
Testing is engineering rigor
- Engineering disciplines other than software can use mathematics, physics and some rules to test if something is feasible, foreseen solution is valid but there is no such thing for software. We build some software and run it. If it works, job is done, otherwise we go back to white board where is the problem. However, we don't need a working prototype to apply tests, our development phase is much cheaper which enables us to apply tests from the start.
Think in states
Above code seems reasonable but actually it lacks implication by the state because a product can't be shipped without payment so
isPaid() is redundant.
- Programming is an art that is done in social and pair programming is extreme of socialism. There are a lot of advantages:
- Weaker developer learns from the experience of the other
- Stronger learns what he can(not) explain
- Equal programmers can find a better solution by discussion, What can one hand do but two hands can make sound (Turkish saying).
- Even if one IDE trick is learnt, that can improve effectiveness by 40%.
- Two bugs can compensate each other and at the end software can work smoothly. However, when one of the bugs is fixed and since the other is still there, fixing a bug can break the working(seem to be) code.
- Code is code through (other) code means coding isn't done alone and my code affects your code. Therefore, even if your code is good quality, since it uses my code, its quality degrades to mine.
Unix Toolset vs IDE
- Without any hesitation, Unix Toolset because
- IDEs are language specific but Unix toolset targets text so universal.
- IDEs make easier some tasks but these functionalities aren't extendable. However, you can use Unix toolset as a lego to create your custom popular commands so your imagination is the limit.
- They come from old days where RAM was very scarce so they were designed to little resource which brings efficiency and speed.
- They are open source and available.
Right algorithm and data structure
- I think there is no need to say anything. At most, learn them not to reinvent and apply in the context.
- Too much logging is as useless as no logging.
WET dilutes performance bottlenecks
- We have a functionality that consumes 30% of CPU so it is bottleneck. However, we have divided functionality into 10 similar parts and Write Every Time (WET). As a result, each part consumes 3% which is reasonable. Instead, if we had used Don't Repeat Yourself (DRY), we could easily detect the problem. Writing every time only transforms the problem to problem factory.
Programmers and Testers should collaborate
- Less time is spent to send bugs back and forth.
- Problem of interest is easily detected as a bug or a new feature.
- Development style goes into Test Driven Development (TDD).
- Insights (from feature requests and code internals) are exchanged.
- Test automation suite could be developed by the help of developers.
- Relationship in between improves which means quality software.
- Write code as if you had to support it for the rest of your life, this is the attitude to internalize all advice because if we get it, we care more what we do and intrinsically try to get better.
Write small functions using examples
- It is more than size/length because a short function can require a lot of values to be tested to be proven as correct. Moreover methods should use knowledge of the domain to reduce ranges.
Write tests for people
- Target of the tests must be people, not ourselves nor the compiler. A good test can be seen as a documentation because since it sets context and preconditions and has the expected values, what is done by the software can easily be followed.
Care about the Code
- Good and clean code is developed by programmers who care about the Code because
- They aren't only interested in if it is working.
- They try to write discoverable(easy to understand and use), maintainable(easy to modify) and correct(not just look like working).
- They leave the code better than they found it.
- They are social and always learn new languages, idioms and techniques.
- Customers don't really know what they want and leave out significant details by assuming you already know pretty minimum to understand. Solution is to interchange frequently with them and possibly getting other senses into sights such as by preparing a prototype.