Categories
Careers

Programmer Best Practices — Clean Code

To be a professional programmer, there’re many things that we’ve to do to be a good one.

In this article, we’ll look at the most basic requirement to be a good programmer, which is writing clean code.

Clean Code

Code is what we have to write to make things that businesses want.

Therefore, we’ll have to live with them for a long time.

This means that we’ve to write code that we can change and read easily.

Otherwise, we’re making our lives difficult.

Writing clean code is a way to make them clear and easy to change.

We just got to strive to write clean code.

Code is the way that we express requirements, so we should make them self explanatory so that we or other people can know what the code means.

Bad Code

Bad code is just code that we can’t live with.

Bad code includes code that has bad naming, messy logic, bad organization, and many more things that make reading and changing it difficult.

Bad code may be faster to make when we’re writing them, but we’ll pay dearly later.

This is because one will understand what we write when we go back to look at it.

Everything is so confusing that making even simple changes would be very difficult.

Therefore, we should always clean up our code after we got something working.

If we leave it, then it’ll haunt us later.

We should clean it up immediately. Otherwise, we’ll never clean it up.

Costs of Messy Code

Since changing bad code is so hard and time-consuming, it’ll definitely cost us more money.

Also, if we make changes, we may break other parts easily.

This isn’t good at all since we customers will notice if the bad parts are released.

Productivity decreases and customer complaints increase with bad code.

There’s just no way that bad code can be good for anyone.

The more change we make to bad code, the messier it gets, and productivity decreases more.

Redesign

Eventually, the code is so messy that making changes is impossible and the code has to be rewritten from scratch.

This takes lots of time since we’ve to replicate all the parts of the existing software.

There are many cases and boundary conditions that we have to worry about.

We always encounter issues when writing code, especially if it’s such a big rewrite.

Therefore, it just takes longer to rewrite the code.

Something that takes minute would take hours, and something take hours would take days.

Changes to one module may lead to changes in 10 or more other modules.

It’s tough to rewrite code.

Therefore, we should always leave time for cleaning up our code from the get-go.

We should give ourselves generous estimates so that we can clean up after ourselves.

And then we won’t run into all the problems that we’ll encounter later on with bad code.

The business probably wants good code so that we can work on them faster.

Therefore, we should think about that when we make estimates.

Making messes to meet deadlines just isn’t a good idea.

The moment we finish writing the messy code, we’ll be confused by them immediately.

Meeting deadlines means that we should write our code cleanly.

Clean Code is an Art

Writing clean code is an art.

We’ve to use lots of little techniques in a disciplined manner to create clean code.

We’ve to acquire the sense of writing clean code.

There are many definitions of clean code. Some may say it’s elegant.

According to Mac’s built-in dictionary app, elegant means ‘pleasingly graceful and stylish in appearance or manner; pleasingly ingenious and simple.’

It’s supposed to be pleasing.

Clean code should be pleasing to read.

We shouldn’t be tempted to turn our code into a mess.

Error handling should be complete. Also, broken bits should be fided.

Clean code does one thing well.

Conclusion

The cost of messy code is just too high.

Therefore, we should write code that is clean and well organized, and easy to read and change.

Clean code makes us work faster and messy code will slow us down to a point where working with the code is almost impossible.

Categories
JavaScript Basics

How to Combine Multiple Strings with JavaScript

Combining strings is something that we have to do often with JavaScript.

In this article, we’ll look at how to combine them with JavaScript.

Plus Operator

The + operator also lets us combine multiple strings together.

For instance, we can write:

const firstName = 'james',
  lastName = 'smith';

const greeting = 'hi' + ' ' + firstName + ' ' + lastName;

This is uglier than the other solutions since we have to use the + sign for each string expression.

Mutative Concatenation

We can concatenate to existing strings.

For instance, we can write:

const firstName = 'james',
  lastName = 'smith';

let greeting = 'hi'
greeting += ' ' + firstName + ' ' + lastName;

Then we concatenate to the greeting string.

We can’t use const to declare greeting since we’re assigning a new value to it.

Template Literals

Template literals are a newer string type that lets us interpolate expressions into them,.

For instance, we can write:

const firstName = 'james',
  lastName = 'smith';

const greeting = `hi ${firstName} ${lastName}`;

We have the firstName and lastName variables.

Then we get 'hi james smith’ as the value of“greeting` .

And we interpolated them into the string expression.

The ${...} is the interpolation symbol.

We can put any expression in between them.

Now we don’t have to worry about looking at ugly concatenation strings.

Array.prototype.join()

Array instances have the join method.

It takes an optional separation to combine the strings with.

If the array entries aren’t strings, then they’ll be coerced to strings.

For instance, we can use it by writing:

const firstName = 'james',
  lastName = 'smith';

const greeting = ['hi', firstName, lastName].join(' ')

We join 'hi' , firstName and lastName with a space string.

And we get the same result as with the previous example.

The separator can be any string.

String.prototype.concat()

Strings have the concat method to let us concatenate various strings into one.

For instance, we can write:

const firstName = 'james',
  lastName = 'smith';

const greeting = ''.concat('hi', ' ', firstName, ' ', lastName);

We have the concat method which has all the strings that we want to join together.

They’re 'hi' , firstName , and lastName with empty spaces in between each of them.

So we get the same result as before.

Escaping Quotes

If we want to use single quotes in single-quoted strings, double quotes with double-quotes strings, and backticks in template literals, then we’ve to escape those characters in those strings.

We can do that with a “ character.

For instance, we can write:

const str = ``hello world``;

or

const str = ''hello world'';

or

const str = ""hello world""

The “ indicates that it should be considered a character in the string rather than a string delimiter.

Conclusion

There are several ways to combine strings together in JavaScript.

Template literals and join method is convenient for joining strings together.

Categories
MongoDB

Using MongoDB with Mongoose — String, Number, and Date Schema Types

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

String Schema Types

We can set various properties for strings schema types.

They include:

  • lowercase: boolean, whether to always call .toLowerCase() on the value
  • uppercase: boolean, whether to always call .toUpperCase() on the value
  • trim: boolean, whether to always call .trim() on the value
  • match: RegExp, creates a validator that checks if the value matches the given regular expression
  • enum: Array, creates a validator that checks if the value is in the given array.
  • minlength: Number, creates a validator that checks if the value length is not less than the given number
  • maxlength: Number, creates a validator that checks if the value length is not greater than the given number

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  test: {
    type: String,
    enum: ['apple', 'orange']
  }
});

to make the test field an enum.

Number Schema Types

For number schema types, we can set the following properties for the field:

  • min: Number, creates a validator that checks if the value is greater than or equal to the given minimum.
  • max: Number, creates a validator that checks if the value is less than or equal to the given maximum.
  • enum: Array, creates a validator that checks if the value is strictly equal to one of the values in the given array.

Date Schema Types

For date schema types, we can set:

  • min: Date
  • max: Date

We can use the schema types by writing:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const personSchema = new mongoose.Schema({
  name: String
});

const Person = mongoose.model('Person', personSchema);
const person = new Person({ name: { toString: () => 42 } });
person.save();
console.log(person.name);

We have the toString method that converts 42 into a string.

The field can also be rewritten as:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const personSchema = new mongoose.Schema({
  name: 'String'
});

const Person = mongoose.model('Person', personSchema);
const person = new Person({ name: { toString: () => 42 } });
person.save();
console.log(person.name);

The value is 'String' instead of the String constructor.

They do the same thing.

Dates

If we have date fields, we can call various methods to change its value:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const Assignment = mongoose.model('Assignment', { dueDate: Date });
  const assignment = new Assignment({ dueDate: new Date() });
  await assignment.save();
  Assignment.findOne((err, doc) => {
    doc.dueDate.setMonth(3);
    doc.save();
    doc.markModified('dueDate');
    doc.save();
  })
}
run();

We call the setMonth method to set the month.

Conclusion

We can set the schema types with various properties when we create the schema with Mongoose.

Categories
MongoDB

Using MongoDB with Mongoose — Connections

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Connections

We can connect to a MongoDB database with Mongoose.

To do that, we write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

We connect to the server with the URL and the test collection name.

Operation Buffering

We can start using the models immediately without waiting fir Mongoose to establish a connection.

So we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const Assignment = mongoose.model('Assignment', { dueDate: Date });
  const assignment = new Assignment({ dueDate: new Date() });
  await assignment.save();
  Assignment.findOne((err, doc) => {
    doc.dueDate.setMonth(3);
    doc.save();
    doc.markModified('dueDate');
    doc.save();
  })
}
run();

We create our model and use it without waiting for the connect to complete since we have the database code outside the callback.

This is because Mongoose buffers the model function calls internally.

However, Mongoose won’t throw any errors if we use models without connecting.

We can disable buffering by setting bufferCommands to false :

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
mongoose.set('bufferCommands', false);
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

We can create collections with the createCollection method:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

async function run() {
  const schema = new mongoose.Schema({
    name: String
  }, {
    capped: { size: 1024 },
    bufferCommands: false,
    autoCreate: false
  });

  const Model = mongoose.model('Test', schema);
  await Model.createCollection();
}
run();

Now we’ll create the tests collection to create the collection.

Error Handling

We can catch any errors that are raised with the catch method or try-catch with async and await since mongoose.connect returns a promise.

For example, we can write:

const mongoose = require('mongoose');
async function run() {
  try {
    await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
  } catch (error) {
    handleError(error);
  }
}
run();

or:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }).
  catch(error => handleError(error));

to connect to a database.

We can also listen to the error event as we did in the example above.

Callback

The mongoose.connect method takes a callback to so we can check for errors.

For example, we can write:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }, function (error) {
  console.log(error);
})

Connection String Options

We can add connection options to the connection string.

For example, we can write:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test?connectTimeoutMS=1000&bufferCommands=false')

We set the connection timeout with the connectTimeoutMS option is set to 1000ms to set.

And we also set bufferCommands option to false .

Conclusion

There are many things we can configure with connections.

Also, Mongoose buffers any commands queued before the connection is made by default so we don’t have to wait for a connection is done before running Mongoose methods.

Categories
MongoDB

Using MongoDB with Mongoose — SchemaTypes

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

SchemaType

A SchemaType is a configuration object for an individual property.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});
console.log(kittySchema.path('name') instanceof mongoose.SchemaType);

to check that the name field in the kittySchema is an instance of mongoose.SchemaType .

It should return true , so we know that name is a SchemaType .

If we have nested fields in a schema, we also have to set the types for the nested fields:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: { type: String },
  nested: {
    firstName: { type: String },
    lastName: { type: String }
  }
});

Schema Types

With Mongoose, we can define a few schema types with our field.

They include:

  • required: boolean or function, if true adds a required validator for this property
  • default: Any or function, sets a default value for the path. If the value is a function, the return value of the function is used as the default.
  • select: boolean, specifies default projections for queries
  • validate: function, adds a validator function for this property
  • get: function, defines a custom getter for this property using Object.defineProperty().
  • set: function, defines a custom setter for this property using Object.defineProperty().
  • alias: string, available with Mongoose >= 4.10.0 only. Defines a virtual with the given name that gets/sets this path.
  • immutable: boolean, defines path as immutable. Mongoose prevents you from changing immutable paths unless the parent document has isNew: true.
  • transform: function, Mongoose calls this function when you call Document#toJSON() function, including when you JSON.stringify() a document.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  integerOnly: {
    type: Number,
    get: v => Math.round(v),
    set: v => Math.round(v),
    alias: 'i'
  }
});

to create a schema with the integerOnly field.

We control how the value is get and set with the get and set methods respectively.

And we added an alias property to define another name we can access it with.

Indexes

We can add indexes for fields. For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const schema = new mongoose.Schema({
  test: {
    type: String,
    index: true,
    unique: true
  }
});

to add the test schema with the index property set to true to add an index for the test field.

The unique property set to true will add a unique index.

Conclusion

We can specify different schema types to set the fields for the schema.