Pages

Sunday, June 15, 2014

Evil Wizards

Wizards are always great! From the time when we started using computers, they take care of many things like software installations and removals (‘The wizard will now install your software!!’).

Wizards also help us in coding, especially while working with IDEs. They write us initial code for setting up a project, or depending on the situation at hand. We can use wizards to create server components, implement Java beans and handle network interfaces - all areas where it’s better to have expert help.

But, never let the wizard to be evil! The code wizard generates may not be required for our program, or may be wrong depending on the circumstances. Be aware of what the wizard writes for us. If we don’t understand the code written, then the program control is not in our hand.

Don’t use wizard code you don’t understand.

This code will also eventually become a part of our application. Always understand what’s there in our program at any point of time.


- summary of Evil Wizards, from  The Pragmatic Programmer: from Journeyman to Master

Wednesday, June 11, 2014

Unit Testing (Part 3)

Writing Unit Tests

Unit tests for a module shouldn’t be located far away from the module. In small projects unit tests can be included in the module itself. But for larger projects, it’s better to move them into a separate directory. But why do we say that it should be closer? Because, anything that’s not easy to find will not be used!

There are two advantages of doing this:

  • It gives some examples on how to use all the functionalities of the module.
  • A mean to build regression tests for validating the future changes in the code.

But providing unit tests is not only enough. We have to run them often, every time when we make changes to our code. It helps us to confirm that the class passes its tests once in a while.

Test Harness

When we write a lot of test code, it’s always better to have a standard test harness to create a testing environment. This helps to make our life easier.

A test harness can be implemented in the same language used for coding, or using makefiles and scripts. It can handle common operations like logging status, analyzing output for expected results and selecting and running the tests. It can also be GUI driven.

No matter however it is implemented, a test harness should have the following capabilities:

  • A standard way to specify setup and cleanup.
  • A method to select individual tests or all the tests.
  • A method to analyze output for expected or unexpected results.
  • A standardized form of failure reporting.

Tests should be always composable. That means, a test can be composed of subtests of subcomponents to any depth. In this way, we can select the complete test, or individual parts of the test depending on the requirement at that time.


summary of Writing Unit Tests & Test Harness, from The Pragmatic Programmer: from Journeyman to Master

Monday, May 19, 2014

Unit Testing (Part 2)

Testing Against Contract

We like to write test cases for a module to ensure that the modules honors its contract. This will tell us
  • whether the code meets the contract.
  • whether the contract means what we think it means.
We test the module to check whether it delivers the functionality it promises, over a wide range of test cases and boundary conditions.

How do we define contract? For example, let’s take the factorial of a number problem. It’s contract can be defined as follows:
  • Pass in a negative argument and ensure that it is rejected.
  • Pass in an argument zero and ensure that it is accepted (boundary condition).
  • Pass in an argument between zero and upper limit and ensure that the correct factorial is returned.
We have to test all these scenarios to ensure that our factorial module is working.

Why do we go to all this trouble? We are investing time and effort to write test cases. All we want is to avoid ‘time bombs’ in our code that sits unnoticed and blows up at an unexpected moment later in the project.

Suppose our module uses a linked list and a sort algorithm, we have to test the linked list, sort and the module completely. If the linked list and sort pass and the module test cases fails, we’ll know that the problem is with the module. This helps to reduce the debugging time also.

While designing a module, always design both its contract and the code to test that contract.


- summary of Testing Against Contract, from The Pragmatic Programmer: from Journeyman to Master

Monday, May 12, 2014

Unit Testing (Part 1)

While writing code, we have to build testability into our software from the beginning itself. This helps us to test each piece of our code before integrating them together.

Unit testing helps us to do this. This is done on each module of our application. Unit testing is performed isolating each module from one another so that the functionality of each module can be tested separately.

Unit testing establishes an artificial environment and invokes the module under testing. It checks the result returned by the module and compares it against a list of known values or against the results from previous runs of the same test. Unit test ensures that in a complete system, individual parts work as expected.

- summary of Unit Testing, from The Pragmatic Programmer: from Journeyman to Master

Wednesday, April 16, 2014

Refactoring (Part 2)

How do we Refactor?

Refactoring can be triggered by many factors such as deeper understandings, changes in requirements and so on. But never try to break into large quantities of code and refactor. We’ll end up in a much worse position than we are.

Refactoring has to be done slowly, deliberately and carefully. Below are some guidelines for refactoring given by Martin Fowler:

  • Don’t try to add functionality and refactor at the same time.
  • Have good tests before refactoring. It’ll help us to detect if anything breaks.
  • Take short steps.

While refactoring, fix the code and everything depends on the code. This will a pain. But it’s going to hurt more later.


- summary of Refactoring, from The Pragmatic Programmer: from Journeyman to Master

Tuesday, April 15, 2014

Refactoring (Part 1)

As the program develops, we will have to rethink earlier decisions and rework certain portions of the code. Code is not static. It needs to evolve.

Rewriting, reworking, and re-architecting code is collectively known as refactoring.

Refactoring.. When should we do that?

Following are some scenarios where we need to refactor our code:

  • Duplication - when we detect a violation of DRY principle.
  • Non-orthogonal design - when we discover that the design can be much more orthogonal.
  • Outdated knowledge - when the knowledge about the problem domain increases.
  • Performance - when we need a much better performance than existing.

Refactoring is not always easy. We have to go through the existing code and modify it without affecting the functionality. Many developers are reluctant to do this because their code is mostly fragile.

Time is another reason for not refactoring. But the reality is that, if we fail to refactor it now, we might have to spend much more time later for fixing bigger problems.

Refactor Early, Refactor Often


Saturday, April 05, 2014

Algorithm Speed (Part 3)

Algorithm Speed in Practice

We may not be dealing with sorting or searching algorithms more often in the real life. But there are situations where we need to think of estimation. While encountering a single loop, it’s easy to identify now that we are dealing with a O(n) algorithm. It is O(n×m) if it has a nested loop.

Estimate the Order of Your Algorithms

If we have an algorithm of order O(n2), we can try to bring it down to O(nlog(n)). If we don’t know how long it takes, the easiest way is to test with different set of inputs and plot a graph. With around 3 - 4 points in the graph, we’ll be able to estimate the order of the algorithm.

But this is not always the case. A simple O(n2) algorithm works better than O(nlogn) for smaller values of n. At the end of the day, what really matters is how long our code takes to execute with real data in the production environment. So, always

Test Your Estimates

In some other cases, the fastest is not always the best to do the job. We have to make sure that the algorithm is apt for our problem, before going any further.


- summary of Algorithm Speed, from The Pragmatic Programmer: from Journeyman to Master