This article will touch base on the engineering aspects of software development and the processes with follow @ Aubergine that makes us unique. We follow a proactive approach to software development and operations instead of reaction-based approach after an issue has occurred. Our attempts are always channeled towards how to avoid problems so as to avoid the questions about how to fix them.
Aubergine works in a few different engagement terms in building software for our clients. In most cases, we handle the entire software development process ourselves with dedicated teams and technical leads working as the CTO for our client. In this model, we have the highest impact because we have the freedom to setup our own processes, of course with client’s buy-in and the engineers are already trained for working most effectively with these processes.
At Aubergine, in all our efforts of building the software, we have a very high priority of minimizing the Technical Debt. Our engineers are trained to minimize technical debt and that pays rich dividends. We have some systems running live in production without any issues for more than a couple of years, even with new changes being actively deployed. Good Engineering is ‘invisible’ i.e. no one blinks an eye if the system runs without any hiccups. The moment it breaks and someone fixes it, the engineer who fixed it is rightly praised, eventhough he/she didnt take enough care about preventing the issue in the first place. At Aubergine, we strive to keep the good engineering ‘invisible’ so that we can minimize the ‘on-call’ duties.
Being in the business since 2013, building reliable, scalable and highly available software products for clients, our beliefs in our processes are getting stronger as we see it work every day. We explain the importance of doing these things and doing them right, to our clients so as to achieve long-term gains for their business.
So how do we achieve this? What does good software engineering entail?
Good software engineering practices invest in creating software that is free from technical debt. A few aspects from my point of view.
- Modularized code with greater reusability for productivity and maintainability gains.
- Code that is architected well, maybe using micro-services for complex distributed systems instead of a monolith
- Having sufficient Unit/Integration Tests which has good code coverage including good conditional coverage (80%)
- Code that is checked using static analysis code-checkers (pep8 for python, findbugs for Java) and fixed for errors. This can be integrated with the CI systems.
- Developers using a continuous integration system like jenkins/babmoo, etc. to execute the tests (API/Front-end) in a continuous manner and use the ‘stop-the-assembly-line’ metaphor if the system raises any alarms about build/test/code convention failures.
- Systems that are designed for doing database updates that require DB transactions to reflect the problem domain accurately.
- Systems that are fail-safe i.e. a failure in one of the sub-systems ends up being a graceful failure.
- Do Retries when feasible
- Defensive programming
- Software that is designed to provide the best performance for all practical purposes with the best effort:benefit trade-off and which works smoothly for all practical purposes
- Code that is not repeated anywhere.
- Code with reasonable cyclomatic complexity.
- Using concurrency to our advantage to optimize code execution/processing times and achieve parallelism
- Using Asynchronous jobs whereever applicable and using callbacks on completion of asynchronous tasks
- Effectively using asynchronous task executors
- Gzip, obfuscate, minify our front-end resources so as to achieve fastest load time
- Use Pagespeed tools for optimizing our front-ends
- When communicating with any DB system and/or writing any query, we understand the “query plan” that the database will use while executing it. Creating the right indexes that will be used for querying.
- We understand the nature of our application i.e. whether it is read-heavy or write-heavy and try to base our design decisions on that.
- Avoid Using multiple queries/multiple back and forth communications to the database for the same request
- Automating DevOps processes/tasks
- Automation of server configuration using Chef/Puppet/Ansible/Docker, etc.
- Achieving true scalability by not only supporting it at the code level, but also easily able to horizontally scale for large number of concurrent access requirements
- Using log visualization and aggregation tools like Splunk, Sentry, etc.
- Creating cloud monitoring Dashboards using AWS Cloudwatch, Newrelic or setup custom stack using Elastic Search, Logstash, Kibana, Graphana, etc. to monitor response times, error rate, traffic rate etc.
- Using Application monitoring stats mentioned above, setup Paging alerts to on-call engineers to get alerted when hitting certain thresholds.
- Monitor Server level stats like RAM usage, Disk Usage, CPU usage, Network IO, etc and setup alerts on reaching certain thresholds.
- Functional testing of the App, mostly achieved by automated tests.
- Use manual testing sparingly but effectively to oversee things that require manual testing/verification.
Load and Performance Testing
- Using tools like JMeter for load testing and measuring the performance of the App under load. It is important to know the threshold after which our application starts degrading non-linearly for a particular hardware configuration? If we already know what our peak load will be under production, it is also important to test for 2x the peak real traffic since if we have two servers in production and one of them goes down.
- Using Profilers to figure out which pieces of the code perform worse.
- Basically, imagine that a monkey is trying to operate the system that you built. Do not assume any intelligence on part of the user and assume a certain set of actions will not happen together.
There are a whole of other things that we need to keep in mind, and we will keep adding them here.
Thank you for reading.. ! Do get in touch with us at firstname.lastname@example.org to inquire about how we can help you build your product.