July 6, 2014
Here are a few quick tips to examine your existing code to identify reuse candidates:
- Introduce Factory or Builder instead of repetitive boiler-plate code when constructing key objects. Several benefits: makes it easy to refactor and evolve how underlying objects are stitched together, makes it easy to write more intention revealing code, and very relevant / convenient when writing automated tests
- Clarify functional behavior and tease out non-functional logic – look at how functional logic is implemented across your codebase. Do multiple projects share a common set of domain assets – objects, rules, services, and the like? if not, look for areas where functional logic is tightly bound to non-functional aspects like fine-grained metrics capture, exception handling, retry / timeout handling, etc.
- Public API that needs to be accessed across platforms / devices are strong reuse candidates – for instance, are there functional APIs that don’t have a remote interface (e.g. a plain Java implementation without an appropriate REST-ful web service resource)? Do you need functionality to be made available across multiple devices with varying User Interface semantics? If so, look to carve the shared logic out into a service that can be accessed from multiple devices / integration points
July 6, 2014
Resisting the temptation to implement a story is very hard for a dev team – it is all too easy to get carried away in introducing a new idea as a reusable asset. However, these are the moments where you must pause and ask yourself a few basic questions before rushing to write code:
- Is this a one-off requirement or part of a recurring theme? Remember – when in doubt, don’t plan for reuse but continuously align and refactor your codebase. Use before reuse!
- Is there a confirmed consumer of this asset beyond the immediate project? If not, keep it in the originating project and promote it for reuse when you find another team or project that needs the functionality
- What are the likely variations that aren’t captured in the existing implementation? are they all really required for day 1, day N ? Don’t implement anything unless there is a business mandate to do so. It is very easy to add code wishing someone reuses it rather than refactoring a known good piece of functionality
- Do you have the bandwidth to develop unit tests, capture the right domain abstractions, and write developer facing quick start documentation? If not, resist the urge to prematurely introduce new code that ends up being technical debt
May 31, 2014
Too many systematic reuse initiatives fail because they fail to get their most important constituency. Below are a few tips to get developers to buy into reuse efforts.
Make every design and implementation artifact available for active engagement and feedback. If you say something must be used as a mandate – make sure your developers can actually use and benefit from that capability. This isn’t about functionality alone – it is about transparency, willingness to involve and incorporate a diverse set of ideas, and most importantly, inviting ongoing dialogue and feedback
Next key aspect is – Do you know the key developer pain points? Is it extensibility? Ease of integration? Testability? Performance? If you don’t really know then don’t thrust a reusable asset on them without working out candidate solutions. Get the developer community to help you be part of the overall solution- chances are, there are existing assets, already in production use. Don’t make he mistake of evangelising a mediocre solution that makes it more difficult for developers who have to work with your software day in and out.
March 9, 2014
Complex services often involve orchestration among external providers – either for security, data enrichment, or a host of other reasons. Your service – on many occasions – won’t be able to function effectively without these external integrations or dependencies. There are a number of ways to de-risk these integrations:
- Use timeouts explicitly – whether it is using JDBC, URL Connections, or FTP connectors – to avoid indefinitely blocking on external invocations. If it is a custom or 3rd party API that doesn’t have such a parameter, use Future task with timeouts as a workaround.
- Introduce Circuit Brakers – if there are multiple calls timing out within a threshold for instance, subsequent invocations can fail fast and ensure valuable system resources aren’t tied up unnecessarily.
- Provide a debug mode or a message capture mode where requests / events being sent to external integrations can be captured for both monitoring and troubleshooting purposes. These diagnostic capabilities are invaluable when your team is trying to ascertain if a problem is related to connectivity, contract, transmission, etc.
- Monitor external connections via health checks – these checks can alert proactively in the event a connection becomes unresponsive/stale prior to your clients facing issues.
- Smoke test deployments – with every single release and every single time prior to turning on business processing. If you find for instance the database your service depends on is unavailable or if external dependencies are not responding back, it is better to find it sooner rather than later.
- Add robust exception handling enabling greater resiliency. If an external service provider comes back online and you can transparently replay pending messages or requests – the system doesn’t need manual support interventions enabling resiliency and reducing opportunity for manual errors. One way to implement this is via a queue – add messages and process them asynchronously (e.g. order placed via web page in an e-commerce site and you get a confirmation email a few moments later)
February 23, 2014
Came across the insight tools on the Netflix Tech Blog – as you build more and more services, having the right set of monitoring tools, obtaining real-time operational status, and ability to perform preventive activities will be critical so you can honor obligations to your service clients.
February 22, 2014
Many systematic reuse initiatives don’t take off the ground because of the over-emphasis on investing in a new set of components. There is all the talk and promise of enhanced productivity, reduced cost, and swift time to market – however, as focus shifts to building a library of components, real delivery suffers and business applications don’t see any material improvements.
My advice: think about extending and integrating your existing codebases more before focusing on building out a reusable library.
In practical terms – refactor, not just for one project or iteration but for every project and every iteration. Continuously refactor and slowly move the code towards an integrated set of functional and technical capabilities. Every time another application needs similar functionality – refactor and remove the duplication. When you are exposing a public API to your application – refactor bits of logic that tightly couple technical infrastructure concerns with functional logic. With discipline, over a few iterations, you can move code around in your existing suite of applications to the point where it will be obvious what is reusable and what isn’t.
Don’t stop there. Ask your stakeholders about how your team needs to add value – what is truly unique, what is your secret sauce? Provide laser-like focus on the unique capabilities your application / systems need to deliver. For everything else, question your code from a financial lens thinking like a portfolio manager – what components to replace with open-source offerings? which components have potential to be truly reusable across multiple applications? which components need to be completely divested out of? Once you get a good idea of what to maintain, what to put more effort in, and what to get out of – you can be more proactive in aligning your refactoring and reuse initiatives with real business value. Remember – systematic reuse is a means to an end. The real goal is to enable innovative products/services/solutions and reduce needless costs for your firm.
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.