November 10, 2013
In an earlier post, I listed reasons why automated tests are foundational for reuse. In this post, want to provide some approaches that will ease automated testing of your components.
- Mock API interactions when using external dependencies. Mocking will reduce runtime dependencies and make your unit tests faster and more robust. Use JUnit with Mockito - mockito has excellent support for a variety of mocking use cases.
- If an external dependency is required from multiple classes, you can define an Adapter that will wrap the external API via an interface. The interface can then be mocked or stubbed and will provide an abstraction layer for your classes. Word of caution: abstractions are leaky and resist the need to wrap every single API provided by the external dependency.
- Use in-memory databases and provide a consistent API for your tests. A common class could initialize and clean up the in-memory db and can be leveraged from tests. Alternatively, it can be provided as an abstract class that your tests can extend. Take the opportunity to standardize location, naming, and directory structure of test resources – if you are using maven for instance, the db related data files can be placed under src/test/resources/db/<db-name>. Finally, this is very useful in ensuring that the database-bound code is indeed testable – forcing the in-memory db test will make technical debt apparent.
- Use db-deploy or some automated database deployment tooling to define and populate databases from tests – these can enable developers to define and execute tests without sharing / corrupting each other’s data. It will also make your database deployment repeatable and well tested eliminating a key deployment risk.
- Provide a common API for routinely used tasks for developers – e.g. APIs that can create test data in in-house / proprietary formats, parse, and populate appropriate data structures will be useful.
- Use JUnit Rule extensions for having a common API for developers – provide a custom rule that will manage the lifecycle of a legacy component or a API that is difficult to use – these are all opportunities to both facilitate testing and add value via reuse.
November 3, 2013
Here are some tips when authoring web service clients:
- Decouple connectivity from request construction. This will isolate variations in input construction and the mechanics of service invocation cleanly separated. Additionally, the request construction might depend on the particular resource – e.g. they can be set of query string parameters or a more complex object structure.
- Connectivity logic should encapsulate the service URL and automatic-retry considerations. The client can automatically retry GET requests specified number of times if invocation encounters a connection timeout. It should also ensure response is OK (either via HTTP status codes or by examining appropriate response-specific data structures).
- Don’t swallow exceptions – the service might return a resource not found or an internal server error – the code that is using the client should be given the flexibility to deal with these exceptions appropriately – the client code shouldn’t assume or mask these exceptions. When in doubt, don’t suppress runtime exceptions.
- Decouple domain logic from service client – domain logic might dictate whether or not a service call needs to be made, or the nature of input resource data, etc. – this logic is more likely to change per the consuming application’s evolving requirements and shouldn’t be hosting service invocation code in the same class.
- Provide reusable API hooks for addressing cross-cutting concerns – such as response time capture and input and output messages – if you want to report response time trends when invoking a service you will not want to clutter this all over the consuming application’s codebase – the client can and should centralize these.
Remember the above is useful whether you are consuming a service or providing clients for your prospective service consumers.
November 2, 2013
Reading the book Code Simplicity by Max Kanat-Alexander. When you have to make lots of design improvement and implementation decisions it is important to keep our solutions simple. Reducing complexity is an important aspect of good code and particularly relevant to systematic software reuse – so hoping to learn new concepts from this book.
You might want to also check out Max’s interview on the rewards of simple code.
November 2, 2013
You can wait for that dream initiative or project to build a whole new set of reusable components that will magically make your teams more productive. The only issue is – it is highly likely that it will be just that – a dream. Instead of planning for systematic reuse, start executing on it by taking a few simple steps. Ask yourself the following questions:
1. Are you capitalizing on identifying and sharing common components with your department / team?
2. Is every project encouraged to continuously refactor and harmonize classes for reducing redundancies? If not, why not?
3. Do you have code that caters to common infrastructural concerns – logging, exception management, alerting, monitoring, metrics.? If yes, is their reuse mandated via common framework hooks that your developers are already using? If not, what is preventing adoption of these concerns into your development stack? Ask your developers and listen to their concerns – you will need to unearth and attack the root causes behind reuse barriers.
4. Do you utilize ad-hoc, informal pairing and code review sessions to identify and harmonize similar / duplicate / redundant classes? If you review code the first time before a project go-live, odds are you either will regret missed opportunities or bemoan the lack of time within your development cycle for making improvements. Key is to intervene early and often and front load your investments for systematic reuse
5. How do you ensure reafactoring to reuse opportunities are tracked? do you create improvement tickets and action them on a best-effort basis or are they managed as part of the product backlog of things that have to get done? If its the former, it will be difficult to make much progress. Creating and tracking tickets will will provide visibility – however, for you to make tangible progress in acting on them you need to partner with developers and development managers to action work on an ongoing basis.
These are just example questions to help you get your journey started and it should be abundantly clear that discipline and continuous alignment is key. If you don’t do anything else, just force your team to converge on a common implementation on key functionality. You will be surprised what discipline can deliver.
September 9, 2013
What is the one key aspect of making your APIs reusable? Simply put – reducing the barriers to adoption. It matters because your target audience – architects, developers, and project managers – are all trying to deliver value to their clients. Iteration after iteration. Here are specific strategies to reduce the adoption barriers:
- Make the public API as intention revealing as possible. Looking at the public interface it must be very clear and unambiguous what the purpose and scope is of the reusable capability.
- Provide Builder or abstract factory APIs to stitch together complex object graphs in a simple fashion. Make it very easy to wire up the default behaviour and provide hooks to replace specific bits of behaviour that is varying. For example – if saving to a file is a default, provide alternative implementations or an interface hook for your client to inject a custom one.
- Make it easy to integrate the capability with other complimentary APIs. These could be in-house, open-source, or vendor APIs – key is – if they are likely to co-exist in a solution, make it simple to wire them together. If your developers use Spring for dependency injection, make your components easy to work with when it. Ditto for in-house frameworks that your teams use heavily.
- For reusable assets that perform orchestration logic provide hooks for injecting custom logic before / after your API behaviour This will be useful for initializing / cleanup logic – specially with legacy components that may need a bespoke set of steps to start them or free up resources on shutdown.
- Provide Null and/or mocking friendly implementations for testability. Your clients need to test their code via automated tests and so do you as the provider – both these needs are met via testable providers. Make it easy for developers to use them with JUnit and make it hard for them to bypass the public APIs.
August 31, 2013
Tip #31 – Inject Common Reusable Capabilities via JUnit Rules
JUnit has an extremely useful extension mechanism – Rules. The @Rule annotation can help provide additional capabilities to your test methods. For example, ContiPerf provides annotations for performance testing. Similarly, you can provide reusable framework hooks for developers to use alongside their test methods.
Some examples where this can be applied – capturing test execution metrics and publishing to a API for offline trending/analysis or setting up plumbing components to facilitate in-memory db testing via H2db, or data folders, etc.
Implementing a JUnit rule is quite straight forward – here’s an article from David Gassner that provides a sample rule implementation.
July 21, 2013
There are certain key interception points during the development process that can greatly increase the likelihood of systematic reuse. Some agile practices can really help:
- Estimating User Stories – constantly seek synergies and ways to connect new requirements with existing ones or those from other projects the team is working on. You know you are successful when the most junior member of the team is finding commonalities and points out areas to explore for systematic reuse. Very often however, reality is starkly different – similar, slightly varying functionality gets implemented over and over again across projects due to time constraints, lack of awareness, and implementation considerations – e.g. legacy system won’t be able to use this component” or “the existing system implementation is hard to refactor…”, etc
- Pair programming – the real strength with pairing is the variety in perspectives. One person can go for depth (identify a very efficient algorithm) and another can focus on breadth (identify existing components to reuse/extend or contribute current work). It is also convenient when pairing to switch perspectives and identify ways to improve the code by eliminating what is not necessary. Hard to expect one developer to be your systematic reuse superstar every time across every project.
- Code reviews – this is a rich area for reuse opportunities because of two key reasons – one, the idea is proven – it is in code and executing – not just vaporware. Equally important, it is a chance to re-look at the implementation with a detached perspective. Discover unspoken design assumptions, needless couplings, and free up code to be more extensible. Look for unnecessary 3rd party library dependencies, code quality violations that compromise layering (presentation code invoking database or data access logic having UI formatting logic, etc.), and constantly eliminate duplicate boiler-plate code. Remember – continuous alignment and not perfection is the intent here.
- Retrospective – when discussing what went well and what needs to improve – explicitly invest in learning the team’s challenges with systematic reuse. Listen for key signals from the dev team – do they have access to source code? how easy or difficult is the existing component library? what functional goal can be achieved faster if there was further investment in a reusable component?