"Maintainability focuses on the ease with which a software system can be maintained. Maintenance of a software system tasks place as changes are made to it after the software is in operation. Maintenance is necessary to preserve the value of the software over time" - Software Architect's Handbook, Joseph Ingeno, 2018
Text
const createUser = (data) => {
const db = new Postgresql(process.env.POSTGRES_URI);
const payload = {};
payload.id = data.id;
payload.name = data.name;
payload.email = data.email;
const Result = db.insert('User', payload);
for (let i = 0; i < 3; i++) {
try {
await axios.post('https://api.mailgun.com/api/v1/emails', {
to: Result.email,
content: 'Welcome... to Jamaica!'
});
break;
} catch (error) {
console.log('retrying');
}
}
firebase.sendPushNotification(data.userDevice, Result);
// Notifies microservice A
new Kafka(process.env.kafkaBroker).sendMessage('userCreated', Result);
// Notifies microservice B
await RPC.notify('userCreated', Object.assign({}, Result));
};import CreateUser from '@/usecases/createUser'
describe('Create User', () => {
it('should create a user', () => {
try {
const command = new CreateUser();
command.execute();
} catch (error) {
expect(error).to.not.exist;
}
});
});What did we achieve?
Lack of consistency can lead to poor communication and complex client code. Having a consistent style of design is the very essence of Conceptual Integrity.
Some times despite high cohesion and low coupling, the system might not have conceptual integrity. This is because the overall style, themes, mood, does not tie it all together.
// 1) src/utils/sendEmail.js
import mailgunClient from 'mailgun-js'
const mailgun = mailgunClient.createInstance({
apiKey: process.env.API_KEY
})
const sendEmail = (payload) => {
try {
var emailContent = {}
if (payload.to) {
emailContent.to = payload.to
}
if (payload.content) {
emailContent.html = payload.content
}
mailgun.send(emailContent)
return { status: 'ok' }
} catch (error) {
if (error.type === "EXPIRED_CREDENTIALS")
return { status: 'expired_credentials' }
return { status: 'error' }
}
}// 2) src/mailing/MailgunSender.js
import mailgunClient from 'mailgun-js';
class MailgunError < Error;
class MailgunExpiredCredentials < MailgunError;
class MailgunSender {
constructor (apiKey) {
this.apiKey = apiKey;
this.client = mailgunClient.createInstance({
apiKey
});
}
send({ to, body }) {
try {
this.client.send({ to, body });
} catch (error) {
if (error.type === 'EXPIRED_CREDENTIALS') {
throw new MailgunExpiredCredentials();
}
throw new MailgunError();
}
}
}Another Example: Data Access Layer Concepts
Strict Definition: Legacy code is code that we have gotten from someone else
Legacy code is simply code without tests.
Working Effectively with Legacy Code. Michael Feathers. 2004
Software that defines the roadmap of a product and the agenda of a team by developing the Stockholm Syndrome on everyone inside and outside the development team, including stakeholders and users by making itself just too hard to change and extend.
Analyze the system use cases.
Identify how those use cases map to the system.
Prioritize the most critical parts of the system that need the most corrective maintenance.
Design a plan to gradually refactor if possible.
Keeps adding value to the business, slowly but more effective if done well.
class NotificationsFacade {
constructor(
emailSender,
notificationsGateway
deviceStore
) {
this.emailSender = emailSender;
this.notificationsGateway = notificationsGateway;
this.deviceStore = deviceStore;
}
sendWelcomeEmail (user) {
const email = new Email({
to: user.email,
from: 'awesome@service.io',
concept: 'Welcome to Jamaica!',
body: 'This is awesome, thanks for joining'
});
this.emailSender.send(email);
}
sendWelcomeNotification (user) {
const device = this.deviceStore.findByUserId(
user.id
);
const notification = new PushNotification({
title: 'Welcome to Jamaica!',
content: 'This is awesome, thanks for joining'
});
this.notificationsGateway.send(
device, notification
);
}
}
class CreateUserLegacy {
execute () {
// thousands of repetitive lines of code
notificationsFacade.sendWelcomeEmail(user);
}
}A characterization test is a test that characterizes the actual behavior of a piece of code. There’s no “Well, it should do this” or “I think it does that.” The tests document the actual current behavior of the system.
const sumOne = (number) => {
return number * 42432 / (42431 + 0.5 + 0.2 + 0.3) + 3;
};
describe('sumOne', () => {
it('Sums three to my number', () => {
expect(sumOne(1)).to.equal(4);
});
});Clean Architecture by Robert C. Martin
Working Effectively with Legacy Code by Michael Feathers
Mythical Man-Month by Frederick Brooks
Code Simplicity by Max Kanat-Alexander
Test-Driven Development by Example by Kent Beck
Software Engineer @Rever
twitter: @aromeronavia