Tari Ibaba

Tari Ibaba is a software developer with years of experience building websites and apps. He has written extensively on a wide range of programming topics and has created dozens of apps and open-source libraries.

Stop using double negatives or nobody will understand your code

This is a big mistake many developers make that makes code cryptic and unreadable.

Don’t do this:

JavaScript
// ❌ Bad: negative name const isNotVisible = doStuff(); const isDisabled = doStuff(); const isNotActive = doStuff(); const hasNoAccess = doStuff(); // ❌ Double negation console.log(!isNotVisible); console.log(!isDisabled); console.log(!isNotActive); console.log(!hasNoAccess);

Double negatives like this makes it much harder to think about the logic and conditionals in your code.

It’s much better to name them positively:

JavaScript
// ✅ Good: positive name const isVisible = doStuff(); const isEnabled = doStuff(); const isActive = doStuff(); const hasAccess = doStuff(); // ✅ Clear and readable console.log(isVisible); console.log(isEnabled); console.log(isActive); console.log(hasAccess);

There is no indirection and your brain takes zero time to parse this.

Just imagine the pain of trying to understand this:

I didn’t not forget about not being unable to use the account.

Lol I couldn’t even understand it myself even though I made it up.

Compare to the far more natural way you’d expect from a sensible human:

I remembered that I could use the account.

Control flow negation

This is a more delicate form of double negation you need to know about:

JavaScript
const isAllowed = checkSomething(); // ❌ Bad if (!isAllowed) { handleError(); } else { // ❌ double negation! handleSuccess(); }

It’s double negation because we’re checking for the negative first.

So the else clause becomes a not of this negative.

Better:

JavaScript
const isAllowed = checkSomething(); // ✅ Fix: invert the if if (isAllowed) { handleSuccess() } else { handleError(); }

The same thing for equality checks:

JavaScript
// ❌ Double negation if (value !== 0) { doError(); } else { doSuccess(); } // ✅ Better if (value === 0) { doSuccess(); } else { doError(); }

Even when there’s no positive condition you can leave it blank — to keep the negative part in the else:

JavaScript
const hasAlreadyFetched = false; if (hasAlreadyFetched) { // nothing to do } else { doSomething(); }

This is great for expressions that are awkward to negate, like instanceof:

JavaScript
// ❌ Bad if (!(obj instanceof Person)) { doSomething(); } // ✅ if (obj instanceof Person) { } else { doSomething(); }

Exception: guard clauses

In guard clauses we deliberately deal with all the negatives first before the positive.

So we return early and avoid deeply nested ifs:

❌ Instead of this:

JavaScript
function sendMoney(account, amount) { if (account.balance > amount) { if (amount > 0) { if (account.sender === 'user-token') { account.balance -= amount; console.log('Transfer completed'); } else { console.log('Forbidden user'); } } else { console.log('Invalid transfer amount'); } } else { console.log('Insufficient funds'); } }

✅ We do this:

JavaScript
// ✅ Much cleaner function sendMoney(account, amount) { if (account.balance < amount) { console.log('Insufficient funds'); return; } if (amount <= 0) { console.log('Invalid transfer amount'); return; } if (account.sender !== 'user-token') { console.log('Forbidden user'); return; } account.balance -= amount; console.log('Transfer completed'); }

See, there’s no hard and fast rule. The end goal is readability: to make code as easy to understand in as little time as possible.

Flags always start out as false

Flags are boolean variables that control program flow.

A classic use case is running an action as long as the flag has a particular value:

JavaScript
let val = false; while (true); // do something that changes val if (val) { break; } }

In cases like this, always initialize the flag to false and wait for true.

Flags should always start out as false.

JavaScript
// ❌ waiting for true -> false let isRunning = true; while (true) { // processing... if (!isRunning) { break; } } // ✅ waiting for false -> true let hasStopped = false; while (true) { // processing... if (hasStopped) { break; } }

This makes so much sense when you understand flags to be a signal — that something is there.

Compound conditions

Negation also makes complex boolean expressions much harder to understand.

❌ Before:

JavaScript
if (!sleepy && !hungry) { console.log('time for gym👟'); } else { console.log('what to do now...'); // ❌ hard to understand when this runs }

This is where De Morgan’s Laws come in:

A powerful rule set for smoothly simplifying complex booleans and removing excessive negation:

JavaScript
let a: boolean; let b: boolean; !(a && b) === !a || !b; !(a || b) === !a && !b;

✅ So now:

JavaScript
if (!(sleepy || hungry)) { console.log('time for gym👟'); } else { console.log('what to do now...'); }

Now we can easily invert the logic as we did before:

JavaScript
if (sleepy || hungry) { console.log('what to do now...'); } else { console.log('time for gym👟'); }

It’s also structurally similar to the English in this way:

The first version was like:

(!a && !b)

It’s time for gym cause I’m not sleepy and I’m not hungry

After the refactor:

!(a || b):

It’s time for gym cause I’m not sleepy or hungry.

That’s how we’d typically say it.

Key points

  • Boolean variable names should be in the positive form. Exception: flags
  • Always check the positive case first in if-else statements. Exception: Guard clauses
  • Use De Morgan’s Laws to simplify negation in compound conditions.

How to console.log WITHOUT newlines in JavaScript

This is a little-known way to console log without newline that many developers have never used.

Let’s say we need to log in a loop like this:

JavaScript
for (let i = 1; i <= 5; i++) { // print number without newline } // Output: 1 2 3 4 5

❌ Unfortunately the normal way fails us:

So what do we do?

What we do: is process.stdout.write:

JavaScript
for (let i = 1; i <= 5; i++) { process.stdout.write(`${i} `); } // Output: 1 2 3 4 5

How is process.stdout.write different from console.log?

Well first of all, console.log IS process.stdout.write!

stdout is the fundamental way every CLI program logs output to the console.

That’s what it uses at its core:

JavaScript
Console.prototype.log = function() { this._stdout.write(util.format.apply(this, arguments) + '\n'); };

But this extra processing makes it so much better for formatting:

  • I can easily inspect objects with console.log without JSON.stringify:

But process.stdout.write fails miserably — it only accepts strings and Buffers:

But there’s something incredible only process.stdout.write can do:

Data streaming:

process.stdout is actually, a stream.

Streams represent data flow in Node.

  • Read streams — data coming from
  • Write streams — data going into
  • Duplex streams — both

Data’s flowing from the file read stream into the stdout stream.

It’s the same as this:

JavaScript
import fs from 'fs'; fs.createReadStream('codingbeautydev.txt').on( 'data', (chunk) => { process.stdout.write(chunk); } );

But pipe is much more natural for stream-to-stream data flow:

Letting you create powerful transformation pipelines like this:

JavaScript
import fs from 'fs'; import { Transform } from 'stream'; // ✅ duplex stream const uppercase = new Transform({ transform(chunk, encoding, callback) { callback(null, chunk.toString().toUpperCase()); }, }); fs.createReadStream('codingbeautydev.txt') .pipe(uppercase) .pipe(process.stdout);

process.stdin

process.stdin is the process.stdout‘s input counterpart — a readable stream for user input.

So see how with a single line I create a pipeline to reflect all my input to me:

JavaScript
process.stdin.pipe(process.stdout);

And when I insert the uppercase transformation into the pipeline:

JavaScript
import fs from 'fs'; import { Transform } from 'stream'; // ✅ duplex stream const uppercase = new Transform({ transform(chunk, encoding, callback) { callback(null, chunk.toString().toUpperCase()); }, }); process.stdin.pipe(uppercase).pipe(process.stdout);

process.stderr

Here to complete the standard stream trio.

  • console.logprocess.stdin
  • console.errorprocess.stderr

Probably also defined this way in Node:

JavaScript
Console.prototype.error = function() { this._stderr.write(util.format.apply(this, arguments) + '\n'); };
JavaScript
console.log('A normal log message'); console.error('An error message');

You can see the difference in the browser:

Although not much difference on Node:

These are 3 powerful streams that let you input, process, and output data creatively and intuitively.

10 one-liners that change how you think about JavaScript

Here’s something most JavaScript developers don’t know:

You can shorten any piece of code into a single line.

With one-liners I went from 17 imperative lines:

JavaScript
// ❌ 17 lines function extractRedirects(str) { let lines = str.split('\n'); let sourceDestinationList = []; for (let line of lines) { let sourceDestination = line.split(' '); let source = sourceDestination[2]; let destination = sourceDestination[3]; let redirectObj = { source: source, destination: destination, permanent: true, }; sourceDestinationList.push(redirectObj); } return sourceDestinationList; }

To a single functional statement:

JavaScript
// ✅ 1 line -- formatted const extractRedirects = (str) => str .split('\n') .map((line) => line.split(' ')) .map(([, , source, destination]) => ({ source, destination, permanent: true, }));

The second is so much cleaner and elegant — you can clearly see how the data beautifully flows from input to output with no breaks.

Let’s look at 10 unconventional JS one-liners that push you to the limits of what’s possible with JavaScript.

1. Shuffle array

What do you make of this:

JavaScript
// ✅ Standard Fisher-Yates shuffle, functional version const shuffleArray = (arr) => [...Array(arr.length)] .map((_, i) => Math.floor(Math.random() * (i + 1))) .reduce( (shuffled, r, i) => shuffled.map((num, j) => j === i ? shuffled[r] : j === r ? shuffled[i] : num ), arr ); // [ 2, 4, 1, 3, 5 ] (varies) console.log(shuffleArray([1, 2, 3, 4, 5]));

The most complex thing for me was figuring out the immutable no-variable version of the swap — and reduce() has a way of making your head spin.

Then there’s this too:

JavaScript
const shuffleArray = (arr) => arr.sort(() => Math.random() - 0.5); const arr = [1, 2, 3, 4, 5]; console.log(shuffleArray(arr));

I see it everywhere but it’s terrible for getting a truly uniform distribution.

2. Reverse string

❌ 8 lines:

JavaScript
const reverseString = (str) => { let reversed = ''; for (let i = str.length - 1; i >= 0; i--) { const ch = str[i]; reversed += ch; } return reversed; }; const reverse = reverseString('Indestructible!'); console.log(reverse); // !elbitcurtsednI

✅ 1 line:

JavaScript
const reverseString = (str) => str.split('').reverse().join(''); const reverse = reverseString('Indestructible!'); console.log(reverse); // !elbitcurtsednI

3. Group array by ID

Grouping arrays by a specific object property:

JavaScript
const groupBy = (arr, groupFn) => arr.reduce( (grouped, obj) => ({ ...grouped, [groupFn(obj)]: [ ...(grouped[groupFn(obj)] || []), obj, ], }), {} ); const fruits = [ { name: 'pineapple🍍', color: '🟡' }, { name: 'apple🍎', color: '🔴' }, { name: 'banana🍌', color: '🟡' }, { name: 'strawberry🍓', color: '🔴' }, ]; const groupedByNameLength = groupBy( fruits, (fruit) => fruit.color ); console.log(groupedByNameLength);

4. Generate random UUID

So many language concepts working together here:

JavaScript
const generateRandomUUID = (a) => a ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace( /[018]/g, generateRandomUUID ); console.log(generateRandomUUID()); // f138f635-acbd-4f78-9be5-ca3198c4cf34 console.log(generateRandomUUID()); // 8935bb0d-6503-441f-bb25-7bc685b5b5bc

There’s basic arithmetic, powers, random values, methods, bit-shifting, regexes, callback functions, recursion, exponentiation… it’s perfection.

5. Generate random hex color

1 line to generate a random hex color:

JavaScript
const randomHexColor = () => `#${Math.random().toString(16).slice(2, 8).padEnd(6, '0')}`; console.log(randomHexColor()); // #7a10ba (varies) console.log(randomHexColor()); // #65abdc (varies)

6. Array equality

Check array equality with a one-liner…

❌ 11 lines:

JavaScript
const areEqual = (arr1, arr2) => { if (arr1.length === arr2.length) { for (const num of arr1) { if (!arr2.includes(num)) { return false; } } return true; } return false; };

✅ 1 line:

JavaScript
const areEqual = (arr1, arr2) => arr1.sort().join(',') === arr2.sort().join(',');

Or✅:

JavaScript
// For more complex objects // and sort() will probably have a defined callback const areEqual = (arr1, arr2) => JSON.stringify(arr1.sort()) === JSON.stringify(arr2.sort());

7. Remove duplicates from array

Shortest way to remove duplicates from an array?

❌ 9 lines:

JavaScript
const removeDuplicates = (arr) => { const result = []; for (const num of arr) { if (!result.includes(num)) { result.push(num); } } return result; }; const arr = [1, 2, 3, 4, 5, 3, 1, 2, 5]; const distinct = removeDuplicates(arr); console.log(distinct); // [1, 2, 3, 4, 5]

✅ 1 line:

JavaScript
const arr = [1, 2, 3, 4, 5, 3, 1, 2, 5]; const distinct = [...new Set(arr)]; console.log(distinct); // [1, 2, 3, 4, 5]

This used to be like the only reason anyone ever cared for Sets — until we got these 7 amazing new methods.

8. Validate email

Email validation one-liners are all about that regex:

JavaScript
const isValidEmail = (email) => /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

But I’ve seen monstrosities like this (more comprehensive):

JavaScript
const isValidEmail = (email) => /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

And even these (most comprehensive) — can you even see it:

JavaScript
const isValidEmail = (email) => /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

It’s probably about being as thorough as possible – let’s say the 1st one catches like 95% of wrong emails, then the 2nd like 99%.

The last one here is actually the official RFC 2822 standard for validating emails – so we’re looking at 100% coverage.

9. Convert JSON to Maps

JavaScript
const jsonToMap = (json) => new Map(Object.entries(JSON.parse(json))); const json = '{"user1":"John","user2":"Kate","user3":"Peter"}'; const map = jsonToMap(json); // Kate console.log(map.get('user2')); // Map(3) { 'user1' => 'John', 'user2' => 'Kate', 'user3' => 'Peter' } console.log(map);

10. Convert snake to camel case

Easily convert from snake casing to camel casing with zero temporary variables.

JavaScript
const snakeToCamelCase = (s) => s .toLowerCase() .replace(/(_\w)/g, (w) => w.toUpperCase().substr(1)); const str1 = 'passion_growth'; const str2 = 'coding_beauty'; console.log(snakeToCamelCase(str1)); // passionGrowth console.log(snakeToCamelCase(str2)); // codingBeauty

Final thoughts

Countless operations jam-packed into a single statement; A race from input start to output finish with no breaks, a free flow of high-level processing. A testament of coding ability and mastery.

This is the power and beauty of JavaScript one-liners.

New native nested CSS feature in 2024: An absolute game changer

The new native nested CSS feature changes everything for frontend development and makes SASS & LESS useless.

❌ Before:

How would you style the nested elements in this HTML?

HTML
<section> Hi! <div> <p><span>codingbeautydev.com</span> -- coding as a passion</p> Coding helps you achieve a sense of purpose and growth </div> </section>

You’d normally stress yourself and waste a lot of time repeating the outer element names.

No wonder SASS and LESS became so popular.

CSS
section { font-family: Arial; } section div { font-size: 1.5em; } section div p { color: blue; } section div p span { font-weight: bold; }

✅ But now: with native CSS:

CSS
section { font-family: Arial; div { font-size: 1.2em; p { color: blue; span { font-weight: bold; } } } }

So much cleaner and easier. All the styles are now encapsulated together instead of being scattered all over the place.

As intuitive as encapsulation in object-oriented programming:

JavaScript
// ❌ redundancy const personName = 'Tari Ibaba'; const personSite = 'codingbeautydev.com'; const personColor = '🔵blue'; // ✅ encapsulate field class Person { name = 'Tari Ibaba'; site = 'codingbeautydev.com'; color = '🔵blue'; }

In some browsers, you’ll need to use &:

CSS
section { font-family: Arial; & div { font-size: 1.2em; & p { color: blue; & span { font-weight: bold; } } } }

What about classes and IDs?

What if you wanted to style nested elements by their class or id attribute?

HTML
<section class="class1"> Hi! <div id="id1"> <p class="class2"> <span id="url">codingbeautydev.com</span> -- coding as a passion </p> Coding is cognitively challenging and mentally stimulating </div> </section>

The nested CSS will be quite similar:

CSS
.class { font-family: Arial; #id1 { font-size: 1.2em; class2 { color: purple; #url { font-weight: bold; } } } }

It also works with sibling selectors:

CSS
div { section { + p { color: blue; ~ p { color: red; } } } }

Pseudo-classes and elements

Yes:

CSS
button { background-color: blue; :hover { background-color: yellow; } }

Media queries

Another huge selling point of nested CSS:

❌ Before:

Creating media queries was messy and the query styles and main styles for an element were separated:

CSS
.hamburger { display: none; } .header { font-size: 40px; } @media (orientation: landscape) { .header { font-size: 32px; } @media (max-width: 480px) { .hamburger { display: block; } .header { font-size: 24px; } }

✅ Now:

It makes more intuitive sense for the element styles to contain query styles — than for the query styles to contain small segments of the element styles.

Nested CSS lets you do this easily:

CSS
.hamburger { display: none; @media (max-width: 480px) { display: block; } } .header { font-size: 40px; @media(orientation: landscape) { font-size: 32px; } @media(max-width: 480px) { font-size: 24px; } }

With native nested CSS you can create styles in a more intuitive manner.

SASS is practically useless now — especially now that we also have native variables in CSS:

CSS
$base-font-size: 16px; $gutter-width: 10px; .container { padding: calc($gutter-width * 2); // Use calc with Sass variable font-size: $base-font-size; } .heading { font-size: calc($base-font-size * 1.5); // Modify base font size with calc }

“React” developers don’t exist

There is no such thing as a React developer — don’t ever call yourself that.

This is a huge mistake many software developers make that wastes so much of their time.

Focusing on tools instead of concepts. Losing sight of the big picture.

The fact is:

React is just a JavaScript tool. JavaScript is just a computing tool. Computing is just a problem-solving tool.

When I first started coding I was obsessed with languages and their specific syntaxes and APIs.

In fact once I hit a tiny learning roadblock I would switch to a new language expecting things to be different. And then again and again.

Not realizing that every language is fundamentally the same and built on the same core concepts!

  • Input processing output storage.
  • Selection sequence iteration.
  • Data and actions: Create Read Update Delete (“everything is CRUD”).
  • Divide & conquer: modules functions OOP…

There is no programming language anywhere that isn’t based on these core concepts. These are what you should focus on.

These are the abstract foundations of computing — of problem-solving in general.

That is why once I learned one language it was so much easier to learn any new one on the fly.

And the same thing for our natural languages — tools for communicating with other humans.

I’m learning French now and I’m seeing clear core components found in English and every other language:

  • Parts of speech: nouns, verbs & tenses, etc.
  • Grammar rules & idiosyncracies
  • Pronunciation & accent
  • Cultural idioms & slangs: that you’ll never know if you learned the language in a formal setting.

And the same thing for frameworks.

Some devs get so hung up on the specifics of React-Vue-Angular instead of focusing on the foundations:

  • Components & lifecycle: divide & conquer
  • State & updates: data flow & actions
  • SPA routing: navigation, etc.
  • Rendering: interpolation, conditional, etc.

Another mistake I made — reading and re-reading C++ books so I wouldn’t “forget” all those important string functions.

Why worry about the API specifics when you can easily look them up on Google?

And if it’s so crucial to be at your fingertips — like useEffect — that’s going to eventually happen when you search and use it enough times.

Because forgetting is a feature, not a bug.

It’s just like how many of us were taught all sorts of useless/esoteric things in school.

Instead of essential knowledge and skills to succeed in the real world:

  • How to learn, think, and solve problems
  • Social skills: negotiation, empathy, listening, etc.
  • Financial skills: Career decision, managing cashflow, investing, etc.
  • How to find fulfillment in life by setting goals and defining personal values

So programming is thinking not typing.

The tool you use to bring your thoughts into reality is less important than developing those thoughts in your brain.

10 tips to supercharge VS Code and code 10x faster (0 to 100)

95% of developers are just wasting the potential of VS Code.

It has so many hidden gems that upgrade your productivity and quality of life — but they’re not using them.

Use these 10 solid tips to gradually supercharge VS Code and code faster: with powerful extensions, little-known keyboard shortcuts, beautiful fonts & colors, and so much more.

1. First things first

Starting with the essentials.

Learn important keyboard shortcuts

You will code so much faster once you learn these

  • Copy line with Ctrl + C
  • Highlight code left & right with Shift + Left / Right
  • Move line up / down with Alt + Up / Down
  • Delete line with Ctrl + K
  • Close file with Ctrl + W
  • Close all files with Ctrl + K, W

Enable autosave

To save your files automatically.

I’ve talked about why I dislike autosave before, but it’s a great feature to stop the constant Ctrl + S’s.

Set up outline view

The Outline View gives you a quick overview of all the symbols in your active file from the File Explorer Pane:

But I recommend you move to a separate pane to have more space:

2. Set up source control

You need to set up integrations for Git to easily track your changes.

Init repo with VS Code

Use the Source Control Pane to initialize a new local repo.

Commit changes

You can quickly stage your files and press Ctrl + Enter anytime you need to commit:

Install this extension

Now install the GitLens extension for even better integration:

GitLens will show you essential repo data and info on your current file — file history, commits, branches, and more.

Place the cursor on any line in the editor and GitLens will display info on the latest commit where the line was changed:

3. Code faster with AI

GenAI has booming recently and now we have extensions that give you intelligent AI code completions as you type.

And IDE-integration chatbots, some of which use context from your codebase.

Great ones you can try:

  1. GitHub Copilot: paid, $10/month
  2. Coding Beauty Assistant: free, $10 per month for more features
  3. Tabnine: has free version, $12 per month

4. Learn snippets to code faster

Emmet

With this I type much faster when working with JSX, CSS, HTML, and more.

One thing you’ll notice is how similar the abbreviations are to CSS selectors.

This is by design: Emmet syntax is inspired by CSS selectors.

Install this extension

JavaScript (ES6) code snippets: fully loaded with heaps of time-saving code snippets for JavaScript.

See how the imp and imd snippets quickly import the modules with the ES6 syntax:

5. Install keymaps and feel at home

Priceless if you’re used to another code editor.

So if you’re coming from:

  1. Vim: Install this extension
  2. Atom: Install this extension
  3. Visual Studio: Install this extension

For example in Atom, you could easily press A and Shift + A to create a new file or a new folder.

Installing the Atom keymap instantly brings this shortcut to VS Code — no need to manually edit hotkeys:

6. Start auto-linting code

You should install a linter to avoid errors and enforce style.

Use ESLint to quickly find and fix problems in your JS code and easily avoid poor coding practices — like unused vars:

7. Enable auto-formatting

Install Prettier to automatically format your code with consistent formatting.

With Format On Save, Prettier instantly formats your code when you press Ctrl + S.

You can enable it in Settings:

8. Set up debugging and testing

So VS Code already gives you fantastic debugging features.

Set breakpoints, inspect the call stack, watch variables and more.

Testing

These extensions will make testing much easier:

Clicking Run runs the particular test case with no need for the terminal.

9. Use a more code-friendly font

If you look closely you’ll see that I’m NOT using the default Consolas font.

I’m using Cascadia Code — a beautiful font from Microsoft with aesthetically pleasing code ligatures.

Ligatures:

Fira Code is another great one I’ve used:

You can easily change your font in settings:

10. Beautify your editor with colorful themes

Color themes

Set up a beautiful theme to easily modify the colors of all the aspects of the UI.

Including your code’s highlighting:

Some great themes I’ve used:

You can change themes with the Settings icon:

Icon themes

The way VS Code displays icons in the File Explorer Pane is pretty underwhelming:

That’s why you should install VS Code Icons and spice things up:

The distinct icons make it much easier to understand every file and the purpose they play in the codebase.

Go from 0 to 100 in VS Code

Final thoughts

With all these new settings & extensions, you’re all set up to build rapidly and achieve your dev goals much faster.

The 5 most transformative JavaScript features from ES13

ES13 was packed with valuable features that completely transformed the way we write JavaScript.

Code became cleaner, shorter, and easier to write.

Let’s check them out and see the ones you missed.

1. Top-level await

With ES13 we were finally able to use await in the global scope!

❌ Before ES13:

JavaScript
// ❌ Syntax error: await is only valid in async functions await setTimeoutAsync(3000); function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve('codingbeautydev.com'); }, timeout); }); }

We always had to put it in an async function or create an async IIFE:

JavaScript
// async IIFE (async () => { await setTimeoutAsync(3000); })(); // à la C++ async function main() { await setTimeoutAsync(3000); }

✅ After ES13:

JavaScript
// ✅ Waits for timeout - no error thrown await setTimeoutAsync(3000); function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve('codingbeautydev.com'); }, timeout); }); }

2. Class declaration upgrades

Class field declarations

Before ES13 we could only declare class fields in the constructor!

Unlike in many other languages we could not declare or define them in the outermost scope of the class.

❌ Before:

JavaScript
class Site { constructor() { // ❌ wouldn't work outside this.url = 'codingbeautydev.com'; this.color = '🔵blue'; } } const site = new Site(); console.log(site.url); // codingbeautydev.com console.log(color); // blue

✅ Now with ES13:

Just like in TypeScript:

JavaScript
class Site { // ✅ no constructor needed url = 'codingbeautydev.com'; color = '🔵blue'; } const site = new Site(); console.log(site.url); // codingbeautydev.com console.log(color); // 🔵blue

Private methods and fields

Creating private methods was impossible before ES13.

We also had to the ugly underscore hack to indicate privacy — but that was just an indication.

❌ Before:

JavaScript
class Person { _firstName = 'Tari'; _lastName = 'Ibaba'; get name() { return `${this._firstName} ${this._lastName}`; } } const person = new Person(); console.log(person.name); // Tari Ibaba // We can still access private members! console.log(person._firstName); // Tari console.log(person._lastName); // Ibaba // They can also be modified person._firstName = 'Lionel'; person._lastName = 'Messi'; console.log(person.name); // Lionel Messi

✅ Now after ES13:

We can add private fields and members to a class by prefixing it with a hashtag (#):

You’ll get a syntax error if you try to access it from outside the class:

JavaScript
class Person { #firstName = 'Tari'; #lastName = 'Ibaba'; get name() { return `${this.#firstName} ${this.#lastName}`; } } const person = new Person(); console.log(person.name); // SyntaxError: Private field '#firstName' must be // declared in an enclosing class console.log(person.#firstName); console.log(person.#lastName);

We can see something interesting from the error message:

The compiler doesn’t expect you to even try to access private fields from outside the class — it assumes you’re trying to creating one.

Static class fields and static private methods

Static fields — properties of the class itself rather than any specific instance.

Ever since ES13 we can now easily create them for any class:

JavaScript
class Person { static #count = 0; static eyeCount = 2; static getCount() { // Access fellow static member with this return this.#count; } // instance member constructor() { // Access static member with this.constructor this.constructor.#incrementCount(); } static #incrementCount() { this.#count++; } } const person1 = new Person(); const person2 = new Person(); console.log(Person.getCount()); // 2

3. Array upgrades: new at() method

So usually we’d use square brackets ([]) to access the Nth of the array.

JavaScript
const arr = ['a', 'b', 'c', 'd']; console.log(arr[1]); // b

But accessing the Nth item from the end was always a pain — we had to index with arr.length - N:

❌ Before ES13:

JavaScript
const arr = ['a', 'b', 'c', 'd']; // 1st element from the end console.log(arr[arr.length - 1]); // d // 2nd element from the end console.log(arr[arr.length - 2]); // c

Luckily ES13 brought a new at() method that solved all that:

JavaScript
const str = 'Coding Beauty'; console.log(str.at(-1)); // y console.log(str.at(-2)); // t

4. Static class blocks

With static fields came static blocks.

To execute code only once, at the creation of the class — just like static constructors in OOP languages like C# and Java.

So you can create as many of them as you want in the class — all the code will run in the order you defined them:

JavaScript
class Vehicle { static defaultColor = 'blue'; } class Car extends Vehicle { static colors = []; static { this.colors.push(super.defaultColor, 'red'); } static { this.colors.push('green'); } } console.log(Car.colors); // [ 'blue', 'red', 'green' ]

5. Error reporting upgrades

So sometimes we catch errors of methods down the call stack only to rethrow it back up the stack.

But when we do this we lose crucial information from the original error:

JavaScript
try { userAction(); } catch (err) { // ❌ doesn't know fundamental cause of error console.log(err); } function userAction() { try { apiCallThatCanThrow(); } catch (err) { // 👇 rethrow throw new Error('New error message'); } } function apiCallThatCanThrow() { console.log('fetching from codingbeautydev.com...'); throw new Error('throwing for no reason'); }

That was why ES13 introduce a new cause property to preserve this important info and make debugging easier:

JavaScript
try { userAction(); } catch (err) { // ✅ now knows what caused the error console.log(err); console.log(`Cause by: ${err.cause}`); } function userAction() { try { apiCallThatCanThrow(); } catch (err) { // ✅ error cause throw new Error('New error message', { cause: err }); } } function apiCallThatCanThrow() { console.log('fetching from codingbeautydev.com...'); throw new Error('throwing for no reason'); }

Final thoughts

Overall ES13 was a significant leap for JavaScript with several features that have become essential for modern development.

Empowering you to write cleaner code with greater conciseness, expressiveness, and clarity.

Why parseInt(0.0000005) returns 5 in JavaScript

An interesting occurrence showing the sheer weirdness of JavaScript:

Just when you thought you knew everything about parseInt:

The funny thing is it returns 0 for four and five zeroes after the decimal point.

But just add one more zero and it starts acting crazy?

The difference from Number is even more stark.

parseInt vs Number — four and five zeroes:

But:

parseInt vs Number — six zeroes:

But we figure it out eventually.

parseInt: The missing parts

By understanding how the parseInt function work:

It takes two arguments: the string and the base or radix.

JavaScript
parseInt(string) parseInt(string, radix)

Look what it does for 6 zeroes when it’s a string:

JavaScript
parseInt('0.5') // 0 parseInt('0.05') // 0 parseInt('0.005') // 0 parseInt('0.0005') // 0 parseInt('0.00005') // 0 parseInt('0.000005') // 0 // 6 parseInt('0.0000005') // 0 parseInt('015') // 15 parseInt('015', 8) // 13

But for Number:

JavaScript
Number('0.5') // 0.5 Number('0.05') // 0.05 Number('0.005') // 0.005 Number('0.0005') // 0.0005 Number('0.00005') // 0.00005 Number('0.000005') // 0.000005 // 6 ! Number('0.0000005') // 0.0000005 Number('015') // 15

But what happens when it’s a number?

A number like 0.0000005?

Step 1: Convert the number to a string

Number gets coerced to a string!

So look what parseInt does to 0.0000005:

JavaScript
String(0.5); // => '0.5' String(0.05); // => '0.05' String(0.005); // => '0.005' String(0.0005); // => '0.0005' String(0.00005); // => '0.00005' String(0.000005); // => '0.000005' String(0.0000005); // => '5e-7' 👈 this is important

Step 2: Do the actual rounding

So:

parseInt(0.0000005) parseInt('5e-7')

And now see what happens for '5e-7' for parseInt:

Why?

Because of how parseInt works: It interprets only the leading portion of the string as an integer value.

It doesn’t recognize e, so it ignores it and everything onwards.

This is same reason why:

  1. parseInt(999999999999999999999) parseInt(1e+21) 1
  2. parseInt(0.0000007) parseInt(7) 7

So Number is your best bet if you wanna avoid surprises when converting a string to a number

And if you need just the integer part, Math.floor() is here for you:

JavaScript
Math.floor(0.5); // 0 Math.floor(0.05); // 0 Math.floor(0.005); // 0 Math.floor(0.0005); // 0 Math.floor(0.00005); // 0 Math.floor(0.000005); // 0 Math.floor(0.0000005); // 0 ✅ perfect

How I get $1000+ freelance clients consistently as a software developer (5 ways that work)

Don’t make the same mistake I did.

I used to spend hours cold-emailing strangers and applying to random low-quality job listings.

I thought it was all a numbers game.

But it was a complete and utter waste of time.

The truth is there are only 5 major strategies that work — and they are NOT easy (sorry).

If you use them blindly like 95% of people do: you will fail.

Or you get flaky, low-quality clients — if you’re lucky.

1. Build your 1st party network the right way

Most ways of networking are just a waste of time for freelancing.

Either they don’t work or it’s not worth it.

I don’t follow the tired clichéd advice of attending network events and meetups.

You will achieve much faster results online.

2 major ways I use:

  • Internal network building

My primary strategy.

I meet new people from people already in my network on WhatsApp and my email contacts.

These people end up either becoming clients or referring me to clients.

But it won’t work when you don’t have any network in the first place.

In that case, you’ll do:

  • External network building

You deliberately search for new people on social platforms — LinkedIn and Twitter are the best.

But you won’t just add any random people — quality matters just as much as quantity.

They need to meet specific criteria:

Example: if you already have a client in mind, you’d search for people in their network — like employees of the company.

2. Join an exclusive freelance network

This was by far the easiest way for me.

Building your network from scratch gives you more control — but it takes a lot of time, effort, and planning.

This is much faster.

In the networks I’m in I get offered at least 3 new freelancing opportunities every single day.

I don’t accept them all but when I do — these are no-nonsense clients with serious budgets.

But the hard part was finding legit networks and getting entry — they’re called exclusive for a reason.

3. Use freelancing platforms properly

Upwork is dead!

No it’s not. If it’s dead then where is their $1,400,000,000 market cap coming from?

You’re using it wrong. Simple.

For a start:

  • Don’t be a generalist:

“Programmer” won’t get you far.

“Next.js web developer” is much better.

“Next.js web developer building AI chatbots” is powerful.

  • Don’t expect fast money

I can tell you for free — starting from scratch on any online platform is no easy feat.

But once you get past a certain point, all the initial focus and consistency will be worth it.

  • Don’t be intimidated by the competition

A job may have 50+ proposals but many of the applicants are just mass-buying connections and spamming with AI.

You’ll stand out greatly if you personalize your cover letter.

4. Create “content”

And by content I don’t only mean posting on social media or blogging.

“Content” also includes your portfolio — on GitHub and more.

It means creating significant projects in your niche and talking about them.

It means sharing your experience and teaching others what you know.

Your discoveries, ideas, opinions, and what you’re passionate about.

You will boost your visibility and improve your chances of attracting great opportunities.

5. Run targeted ads

I put this one last because it’s quite risky.

Without a well-defined strategy, you’ll end up wasting money and getting nothing out of it.

You’ll need to run extremely targeted ads on Google, LinkedIn, and Facebook — and constantly refine your approach to see any real results.

Follow one or more of these strategies with persistence and you’ll eventually find high-quality clients.

Key points

Recap: 5 major ways to find $1000+ freelance clients as a software developer:

  1. Build your 1st party network
  2. Join an exclusive freelance network
  3. Use freelancing platforms properly
  4. Create content and code
  5. Run paid ads

New HTML <dialog> tag: An absolute game changer

HTML will never be the same with the new <dialog> tag.

❌Before:

See how much work it would have taken me to create a dialog 👇

Almost 20 lines of CSS alone:

And that’s just CSS for the dialog functionality — it will still look very basic:

But how about with the new <dialog> tag!

✅ Now:

HTML
<button id="open">Open</button> <dialog id="dialog"> ⚡Lighting strikes the earth 44 times every second! </dialog>
JavaScript
const dialog = document.getElementById('dialog'); const open = document.getElementById('open'); open.addEventListener('click', () => { dialog.showModal(); });

We can even use the show() method to show a non-modal dialog — less intrusive with no backdrop:

JavaScript
const dialog = document.getElementById('dialog'); const open = document.getElementById('open'); open.addEventListener('click', () => { dialog.show(); });

Dialogs have always been a powerful way to forcefully seize your user’s attention and slam information in their faces.

It’s been a staple feature of every UI design system from Material Design to Fluent Design.

But even as common as they are we always had to resort to third-party libraries or create custom components to use them.

And many of these libraries don’t even follow the official recommendations for usability & accessibility…

Example: pressing the Escape key should dismiss the dialog on the page — but this doesn’t happen for many custom dialogs.

So <dialog> changes all that.

Auto-open dialog

The open attribute keeps the dialog open from the moment you open the page:

HTML
<dialog id="dialog" open> Giraffes are much more likely to be struck by lightning than humans are. In fact, they're 30 times more likely </dialog>

Auto-close button

Yes, you could add close functionality with standard event listeners and the close() method:

JavaScript
const close = document.querySelector( '#dialog .close' ); close.addEventListener('click', () => { dialog.close(); });

But the built-in <dialog> makes this even easier — no JavaScript needed:

HTML
<dialog id="dialog"> ⚡Gain essential coding skills & knowledge at codingbeautydev.com <br /> <form method="dialog"> <button class="close">Close</button> </form> </dialog>

How to style <dialog> the right way

<dialog> has a special ::backdrop pseudo-element for styling the backdrop:

CSS
::backdrop { background-image: linear-gradient( 45deg, magenta, rebeccapurple, dodgerblue, green ); opacity: 0.75; }

Styling the main element is straightforward:

CSS
dialog { background-color: black; color: white; }

Final thoughts

With the new HTML <dialog> tag, creating modals and dialogs in our web apps has never been easier and faster.