Friday, May 10, 2019

Refactoring Wisely

Refactoring is usually defined as changing code without changing the observable behavior of functionality. This is an important distinction because it is very easy to go overboard and start fixing bugs and changing behavior of the software. When several things start changing it is easy to lose control and the refactoring turns into fixing everything which will take a lot of time.

Do one thing at a time, make a note of the other things that would be useful to do and complete the refactoring before moving on to those.

When refactoring it is useful to have tests to ensure that the software will continue working in the same way as before. Automated tests have obvious benefits but they will also add their own maintenance overhead.

If an automated test for a refactoring is considered not beneficial in the long term it can still be used for testing the refactoring and it can be removed after that.

Large refactoring attempts are very risky. If possible, change one component at a time, keep the software working, have the change reviewed and then move on to the next.

Often there is no time available for large refactorings anyway so it is often best to incorporate small refactoring into daily workflow. Leave the codebase in a bit better condition than what it was when you found it.

Also remember that previous programmers probably had less information about the software than you now have. And remember that people prioritizing tasks rarely have enough competence or information about the code to make wise decisions about the amount of refactoring that needs to be done. The only ones in good position to make good decisions about maintenance are the people doing the maintenance.

Sunday, November 30, 2014

Developer Skills

What skills new developers are often lacking:

  • Basic project skills
    • time management
    • effective meetings
  • Platform
  • Server maintenance
  • Development environment
  • Version control
  • Task management
  • Practises
    • Reviews
  • Object-oriented design
    • Liskov SP, Law of Demeter 
    • Interfaces
  • Patterns
    • Design
    • Architectural

What even old developers are often lacking:

Good bug reporting
  • What you did, what happened, what should have happened
Clarity of code
  • Naming
Good object-oriented design
  • SOLID
  • Information hiding
Design
  • Underengineering vs. overengineering
  • Discussion in terms of
    • flexibility
    • clarity
    • complexity
    • development time
In-depth understanding of version control
  • e.g. in Git terms
    • merge
    • rebase
    • bisect
    • status and logs
  • Parallel development models
    • Feature branch and pull request
    • Development branch with continuous integration
    • Release branches
Advanced IDE features
  • Regex search/replace
  • Navigating to related parts in the codebas
  • Automatic refactoring
  • Commenting
Debugging of problems
  • Reproducing problems
  • Manual binary search
  • Checking logs
  • Log correlations (what was logged in different places at the same time)
  • Debuggers
  • Profilers

How to really stand out from other developers:

  • Read books
  • Contribute to open source

Cron Checklist

Short list

  • Schedule syntax
  • Empty line
  • User has permissions
  • Shell command combination problems
  • Command works as the user running cron
  • cron daemon is running 
  • cron runs scripts
  • Programs are defined with full path
  • Programs might use other programs which are not in PATH
  • Job isn't scheduled only one minute in the future
  • Job is run from right directory
  • Check mail
  • Check logs
  • User has a shell defined
  • Percent signs
  • Day of week and day of month combo
  • User
  • Environment
  • Timezone

Verbose list

  • Refer to the manual to check the correct scheduling syntax
  • Make sure there's an empty line at the end
  • Make sure the user has permissions to run cron jobs
  • Make sure you're not assigning environment variables just once while using shell constructs which combine or separate the commands, such as &&, || or ;
    • This will only have FOO=bar assigned in the first script.sh 
      • FOO=bar sh script.sh && sh script.sh
  • Check that the command works as the user who will run the job
  • Check that cron daemon is running
  • Test that the cron job is actually ran by using a test script which writes into a temporary file
  • Verify that programs are defined with their full path, e.g., /bin/grep instead of just grep
    • cron runs with a very minimal environment so it might not have a proper path set
    • a program might also run another program which is not in PATH
  • Setting the cron job just one minute to the future does not work with many implementations
  • Check that the cron job is run from the right directory (cd to the proper directory in the job if needed)
  • Check if cron has sent mail about its failure
  • Check logs
  • Make sure the user who will run the job has a shell defined
  • Look out for per cent signs (%)
  • Check that you're not setting day of week and day of month together. There's a bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=460070 
    • "The other fields are combined with a logical AND (ie: month AND day of month AND hour AND minute), whereas the day of month and day of week fields are combined with a logical OR." Use a shell command to check the date: "`date +w`" -eq 5 &&
  • Check the user who is running the cron job
    • Look at the profiles and other shell resource files
  • Check the environment
    • Create a cron job which will dump the environment to a file (env > /tmp/cron-environment command) and maybe diff it with a working environment
    • Try the command out on the terminal with a regular Bourne Shell (started with the command sh)
  • Cron could be run as a different user who has a different timezone. Also check environment variable CRON_TZ

Advanced Editing

Many programmers don't use their editor's advanced features, a fact which increases the time used in editing and the frequency of errors. Using advanced features makes code editing much more comfortable, faster and safer. Therefore, try to find out about the capabilities of your code editor. Some examples of the advanced features editors provide are:
  • indent a visually selected range
  • commenting/uncommenting a visually selected range
  • deleting a whole line
  • deleting a string inside quotation marks
  • searching for a string under cursor
  • searching with a regex
  • searching and replacing with a regex
  • completing a partial method name
  • jumping to the definition of a class or a method
  • refactoring tools which change the design of code to another one, yet preserve its behavior. e.g. move a loop into a method of its own and make sure all the variables still work like they did before
More information

Online

Literature

  • Pragmatic Programming (3.16. "Power Editing") by Andrew Hunt and David Thomas
  • Code Complete (Chapter 30. "Programming Tools") by Steve McConnell

Reporting Problems

Search
First search the problem tracking database in case a previous report of the issue already exists.

Summary
Write a good summary. Keep in mind that people often look at lists of dozens of problems so the summary must be short yet make the issue stand out from other issues.

Description
Keep the report short and to the point. Include additional information in attachments or divide the report to two parts, the first one a simple explanation and the latter one containing abundant information.

Three things are of extreme importance while reporting:
  • what you did
  • what happened
  • what should have happened

Stating these enable the next reader to reproduce it, which is essential since the problem can't be corrected if it can't be reproduced.

Minimal case
It will probably save time to find a minimal test case that reproduces the problem. Try to repeat the same actions but leave out possible optional steps and try things in a different order.

URI
If there are URIs involved such as web URLs or filesystem paths, include them. Even if it is obvious how to find the problem, it might not be obvious two months from now or to someone just learning the system. Even if the URI is the result from a redirection and no longer points to correct location, it is better to have one than not.

Logs
If there are logs available, check them and include possible information about the issue.

Severity
Assess the severity of the problem:
  • is the problem already visible to clients?
  • does it make completing a task impossible?
  • does it make completing a task more difficult?
  • does it cause failures in other areas?
  • does it make the program look unprofessional?
  • does it cost time or resources from people?
Also see Describing a Problem.

More information

Online

Literature

  • Testing Computer Software (Chapter 5.) by Cem Kaner et al. (1999)

Describing Problems

Unambiguousness
Value precise and unambiguous reporting over using good language. For instance, repeating the same word many times in one sentence might be bad language, but if it clarifies the report reiteration is perfectly acceptable.

A bad example:
"The program started slowing down and the window froze, after which it disappeared."

A good example
"The program started slowing down and the window froze, after which the window disappeared. "

Note how the bad example seems like it uses the language better but it might lead someone to think that the whole program disappeared, which didn't happen.

Generalizations
Do not make unjustified generalizations. 
Bad:
 "This happens with all windows."
Good:
 "This happened while using window A and window B."

Note that the good example doesn't state any assumptions about other areas of the system and only expresses what did happen.

Some things might sound obvious but do state them in the report if there's a chance for misunderstanding.

Conclusion
Keep in mind that those reading the report may not have the same knowledge about the system as you and the report might be read months from now, in a hurry, while fixing a production issue or by someone working long hours. Also see Reporting Problems.

Commenting Code

Reason
Code does not automatically become better by adding more comments. Comments can help only if used well and useless comments actually decrease the undestandability of code.

Intent
Comments are needed when you need to state the intent of the code. Spell out why the code is done the way it is.

Non-obviousness
Do not state obvious things in comments. If you wish to communicate what the code does, use meaningful names for variables, routines, classes and other symbols. If it is possible, try to make the source code itself say what you try to write in comments.

Extracting some code and putting it into its own function is often a good way to say what the code does

Overview
Comments are also useful in explaining blocks of code that are not enclosed inside routines for some reason. For instance, a complicated algorithm might be wise to keep as a whole in one routine, but some of its parts might require comments that might give a useful overview.

Legacy
Legacy code might be too risky to refactor and comments can then be used with minimum risk. But this is still a bad situation since it's usually better to restructure the code. Javadoc-style document comments are often considered good practise.

More information

Online

Literature

  • Code Complete (Chapter 32. "Self-Documenting Code") by Steve McConnell
  • Refactoring to Patterns by Joshua Kerievsky