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.