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.

Nobody wants to use these HTML tags 😭

Trust me, you can do MUCH better than <div> <a> and <p>.

Living images, built-in dialogs, a href hacking…

There’s a whole lot of sophisticated tags to discover.

1. progress and meter

So first there’s progress – your typical drama-free progress bar.

You set a value and max for gradation — pretty straightforward.

HTML
<label for="file" >Downloading knowledge into evil AI</label > <progress id="file" value="0" max="100">32%</progress>

But then there’s meter — also known as progress on a rampage:

HTML
<label for="energy">energy levels after 🍉🍌🍇</label> <meter id="energy" min="0" max="100" low="25" high="75" optimum="80" value="50" />

2. dfn

dfn — for anything we’re gonna define in the page:

HTML
<div> <dfn>Mellifluous</dfn> sounds are smooth, musical🎶, and pleasant to hear </div>

And the definition must be inside the dfn‘s parent tag, or else…

Or else nothing — just a semantic rule you can happily disregard.

3. dialog

New native HTML dialogs!

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

Stay open from the get-go:

HTML
<!-- btw setting open to "false" won't do anything --> <dialog id="dialog" open> 😻 Cats have over 30 different muscles just in their ears, allowing them to swivel them in all directions. </dialog>

Built-in support for closing:

HTML
<dialog id="dialog" open> Your free trial is over! Subscribe for as low as $5 billion per month <form method="dialog"> <button>Get lost</button> </form> </dialog>

And powerful, flexible customization letting you create wonderful UIs like this:

4. map and area

<map> and <area> — powerful combo to create clickable areas in images:

HTML
<img src="workplace.jpg" alt="Workplace" usemap="#workmap" width="400" height="379" /> <map name="workmap"> <area shape="rect" coords="34,44,270,350" alt="Computer" href="computer.html" /> <area shape="rect" coords="290,172,333,250" alt="Phone" href="phone.html" /> <area shape="circle" coords="337,300,44" alt="Cup of coffee" href="coffee.html" /> </map>

We call clickable images like these image maps.

5. bdo

Super cryptic acronym here, what does it do?

This 👇

JavaScript
<!-- dir = direction --> <!-- rtl = right-to-left --> <bdo dir="rtl"> 🥺🥰but when I saw you I felt something I never felt... </bdo>

That’s why it stands for: bi-directional override.

6. base

So you know how relative URLs normally work right? 👇

HTML
<body> <a href="/blog/javascript-functional-try-catch" >This is how functional try-catch transforms your JavaScript code </a> </body>

The <a>‘s use the page’s domain to get the full URL they’ll navigate you to:

But what happens when you create a foundational <base> in the HTML?

HTML
<head> <!-- ✅ <base> tag --> <base href="https://www.codingbeautydev.com" /> <title>codingbeautydev.com</title> </head> <body> <a href="/blog/javascript-functional-try-catch" >This is how functional try-catch transforms your JavaScript code </a> </body>

Now they all use the hardcoded value in the <base>‘s href to get the full URL:

Frameworks like Angular and Flutter Web use it too:

7. time

For dates and times:

HTML
My Journal <br/><br/> <time>2:36pm</time> -- I asked her if she wanted to grab a cup of coffee with me <br /> <time>3:50pm</time> -- Still dealing with the brutal rejection

No fancy visuals but it means a lot to search engines.

With time they better understand the dates in your page and produce smarter results.

Especially with the datetime attribute:

JavaScript
<time datetime="2025-01-07">AI singularity</time> is coming sooner than you think!

8. hgroup

<hgroup> is all about family.

It tells the entire world that a heading tag and some content below are related:

HTML
<hgroup> <h1>Maybe this is the end, but if we go out...</h1> <p>We go out together</p> </hgroup>

Family sticks together:

9. kbd

Represents keyboard inputs:

HTML
Over 30 years professional experience with StackOverflow specializing in <kbd>Ctrl + C</kbd> and <kbd>Ctrl + V</kbd>

And speaking of StackOverflow, <kbd> has a nice styling there and other StackExchange sites.

10. cite

Indicates the title of a book, song, movie, or some other creative work:

HTML
One thing I love about <cite>Wednesday</cite> is how Wednesday doesn't care about fitting in

Final thoughts

So these are 10 of the least known and utilized tags in HTML.

Quite useful in various situations despite their low usage.

5 overlooked ways to make $5000/m online as a programmer

Developers are turning code into cash on the internet every day.

Just look at Pieter Levels, a developer making over $300,000 per month from his laptop.

He’s just one of many who combined ingenuity and skill to create tremendous value through the internet.

If you’ve been coding for a while, you can also provide value with your knowledge in several ways.

1. SaaS: Build it once, sell it forever

This is how Pieter makes most of his money.

It’s the holy grail of online income.

Build it once and sell it forever. It’s as passive as it gets.

The hard part is, what are you building? What problem are you solving?

Ideas are not a dime a dozen. Good ideas are rare.

And how do you find good SAAS ideas?

Look into your life.

What are some problems you face regularly online or offline, and can software solve them?

Uber started because the founders found themselves stuck in Paris with no way to find a taxi.

React, Polymer JS, and many other dev tools came about because the existing alternatives just weren’t good enough.

2. Freelancing the right way

I love freelancing.

It’s packed with new and exciting opportunities with unlimited earning potential.

One minute you’re building a sleek landing page for a startup; the next, you’re diving into an AI project for a company on the other side of the world.

But please 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.

I stopped cold applying and focused on building your network instead. Warm.

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

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

An even faster way was by joining exclusive freelance networks.

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

I could have saved all the time and energy spent on the above — but less control and hard to find and get into legit ones.

3. Create “content”

Every line of code you write has a story behind it. Let’s hear it.

Let’s hear how — tools and techniques.

Let’s hear why — your motivations, thought processes, and breakthroughs.

Don’t just throw your knowledge out there; it’s about storytelling.

Share your discoveries, ideas, and opinions; share what you’re passionate about.

And content doesn’t only mean posting on social media or blogging.

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

So it also means creating significant projects in your niche and talking about them.

The point of all this is to broaden your reach.

You either end up earning directly on the platform (like YouTube), or you greatly boost your visibility and attract great opportunities.

4. Coaching: Teach what you know

Nothing beats interactive and personalized learning from another human.

Someone who’s been there done that. They’ve seen it all, they know all the tips and tricks and pitfalls.

If you’ve been coding for more than a few years, this is you. You are the one.

Turn your knowledge into an income stream by teaching others.

And it doesn’t even have to be online. Look into your life.

When I was in uni there were tons of my coursemates who needed someone to guide them on learning coding and the other CS topics.

That was a serious opportunity.

Especially as I was one of the few who had already been coding for quite some time.

5. Patreon and GitHub Sponsors: Rally a community

Donations? Lol

How can anyone make any real money from donations?!

Until you hear of how Caleb Porzio makes over $100K/year from nothing but those same donations🤯

That’s the magic of Patreon and GitHub Sponsors.

By offering exclusive perks—like priority issue responses, educational guides on the project, or early access to your latest builds—you can turn your most devoted fans into your sponsors.

You can even sell advertising space, just like Vuetify does:

It’s not just about code anymore—it’s about building a community around your work, a tribe that’s willing to keep you going.

Final thoughts

Making money online as a programmer is about turning your skills into something bigger than yourself.

Whether you’re a freelancer battling through client projects or a creator gathering an audience, the key is the same: know your value, share it, and let the internet do the rest.

This new JavaScript operator is an absolute game changer

With the new safe assignment ?= operator you’ll stop writing code like this:

JavaScript
// ❌ Before: // ❌ Deep nesting of try-catch for different errors async function fetchData() { try { const response = await fetch('https://api.codingbeautydev.com/docs'); try { const data = await response.json(); return data; } catch (parseError) { console.error(parseError); } } catch (networkError) { console.error(networkError); } }

And start writing code like this:

JavaScript
// ✅ After: async function fetchData() { const [networkError, response] ?= await fetch('https://codingbeautydev.com'); if (networkError) return console.error(networkError); const [parseError, data] ?= await response.json(); if (parseError) return console.error(parseError); return data; }

We’ve completely eradicated the deep nesting. The code is far more readable and cleaner.

Instead of getting the error in the clunky catch block:

JavaScript
async function doStuff() { try { const data = await func('codingbeautydev.com'); } catch (error) { console.error(error); } }

Now we do everything in just one line.

Instead of failing loudly and proudly, ?= tells the error to shut up and let us decide what do with it.

JavaScript
// ✅ if there's error: `err` has value, `data` is null // ✅ no error: `err` is null, `data has value async function doStuff() { const [err, data] ?= await func('codingbeautydev.com'); }

We can tell it to get lost:

JavaScript
async function doStuff() { // 👇 it's as good as gone here const [, data] ?= await func('codingbeautydev.com'); // ... }

We can announce it to the world and keep things moving:

JavaScript
async function doStuff() { const [err, data] ?= await func('codingbeautydev.com'); if (err) { console.error(err); } // ... }

Or we can stop immediately:

JavaScript
// `err` is null if there's no error async function doStuff() { const [err, data] ?= await func('codingbeautydev.com'); if (err) return; }

Which makes it such a powerful tool for creating guard clauses:

JavaScript
// ✅ avoid nested try-catch // ✅ avoid nested ifs function processFile() { const filename = 'codingbeautydev.com.txt'; const [err, jsonStr] ?= fs.readFileSync(filename, 'utf-8'); if (readErr) { return; } const [jsonErr, json] ?= JSON.parse(jsonStr); if (jsonErr) { return; } const awards = json.awards.length; console.log(`🏅Total awards: ${awards}`); }

And here’s one of the very best things about this new operator.

There are several instances where we want a value that depends on whether or not there’s an exception.

Normally you’ll use a mutable var outside the scope for error-free access:

JavaScript
function writeTransactionsToFile(transactions) { // ❌ mutable var let writeStatus; try { fs.writeFileSync( 'codingbeautydev.com.txt', transactions ); writeStatus = 'success'; } catch (error) { writeStatus = 'error'; } // do something with writeStatus... }

But this can be frustrating, especially when you’re trying to have immutable code and the var was already const before the time came to add try-catch.

You’d have to wrap it try, then remove the const, then make a let declaration outside the try, then re-assign again in the catch

But now with ?=:

JavaScript
function writeTransactionsToFile(transactions) { const [err, data] ?= fs.writeFileSync( 'codingbeautydev.com.txt', transactions ) const writeStatus = err ? 'error' : 'success' // // do something with writeStatus... }

We maintain our immutability and the code is now much more intuitive. Once again we’ve eradicated all nesting.

How does it work?

The new ?= operator calls the Symbol.result method internally.

So when we do this:

JavaScript
const [err, result] ?= func('codingbeautydev.com');

This is what’s actually happening:

JavaScript
// it's an assignment now const [err, result] = func[Symbol.result]( 'codingbeautydev.com' );

So you know what this means right?

It means we can make this work with ANY object that implements Symbol.result:

JavaScript
function doStuff() { return { [Symbol.result]() { return [new Error("Nope"), null]; }, }; } const [error, result] ?= doStuff();

But of course you can throw as always:

JavaScript
function doStuff() { throw new Error('Nope'); } const [error, result] ?= doStuff();

And one cool thing it does: if result has its own Symbol.result method, then ?= drills down recursively:

JavaScript
function doStuff() { return { [Symbol.result](str) { console.log(str); return [ null, { [Symbol.result]() { return [new Error('WTH happened?'), null]; } } ]; } } } const [error, result] ?= doStuff('codingbeautydev.com');

You can also use the object directly instead of returning from a function:

JavaScript
const obj = { [Symbol.result]() { return [new Error('Nope'), null]; }, }; const [error, result] ?= obj;

Although, where would this make any sense in practice?

As we saw earlier, ?= is versatile enough to fit in seamlessly with both normal and awaited functions.

JavaScript
const [error, data] ?= fs.readFileSync('file.txt', 'utf8'); const [error, data] ?= await fs.readFile('file.txt', 'utf8');

using

?= also works with the using keyboard to automatically clean up resources after use.

❌ Before:

JavaScript
try { using resource = getResource(); } catch (err) { // ... }

✅ After:

JavaScript
using [err, resource] ?= getResource();

How to use it now

While we wait for ?= to become natively integrated into JavaScript, we can start it now with this polyfill:

But you can’t use it directly — you’ll need Symbol.result:

JavaScript
import 'polyfill.js'; const [error, data] = fs .readFileSync('codingbeautydev.com.txt', 'utf-8') [Symbol.result]();

Final thoughts

JavaScript error handling just got much more readable and intuitive with the new safe assignment operator (?=).

Use it to write cleaner and more predictable code.

OpenAI’s new o1 model changes EVERYTHING

OpenAI just launched a new model (“o1”) and it’s HUGE.

Before now there’d been a lot of mystery about an upcoming “Strawberry” model, subtly hinted at by Sal Altman in a cryptic tweet last month.

The isn’t just another version of GPT. This is something completely different.

OpenAI designed it to excel in tasks that require deeper reasoning—things like solving multi-step problems, writing intricate code (bad news for devs?), and even handling advanced math.

It doesn’t just predict the next word. It’s been trained to “think”.

But didn’t AI models already do this? Not quite.

o1 is different because it’s been trained using reinforcement learning. A training approach that lets the model learn from its mistakes, getting better over time at reasoning through complex tasks.

How good is it?

OpenAI tested o1 on International Mathematics Olympiad problems and results were jaw-dropping: o1 correctly solved 83% of them.

And how many was last year’s all-powerful GPT-4 able to solve?

13%.

Literally 13 — imagine how bad GPT-3 would have been? You know that wowed us all in 2022. That’s not just an improvement—it’s a massive leap.

Also massive enough to make it as intelligent as Physics and Chemistry PhD students in benchmark tests — incredible.

The price tag

Here’s where things get a bit tricky.

If you’re looking to use o1 through the API, be ready to pay.

The cost is $15 per million input tokens and $60 per million output tokens. Compare that to GPT-4’s $5 and $15, and you can see the difference.

For large scale apps with tons of users this adds up much quickly.

Is it worth it? Well that depends.

o1 is slower when handling simple tasks. So if you just want to know the capital of Spain then it’s a overkill. There’s no thinking there, just basic memory retrieval.

GPT-4 is still more efficient for everyday queries with moderate complexity.

How does it work?

What really makes o1 stand out is how it thinks through problems.

Instead of just spitting out answers, it explains its reasoning step by step. It’s like watching someone carefully work through a tough math problem, showing their process as they go.

OpenAI ran a demo where o1 solved a logic puzzle involving the ages of a prince and princess.

The model broke down the puzzle in real time, walking through the logic in a way that felt almost human. It didn’t just give the answer—it explained how it got there. This “chain of thought” approach gives o1 a serious edge when tackling complex challenges.

It’s kind of like an LLM agent, break down goals into sub-goals, possibly doing some internal multi-prompting.

Or like AutoGPT (lol) — you know that overhyped stuff no one talks about anymore.

But it’s not perfect. When you ask it simple questions, it can overthink things. For instance, ask it where oak trees grow in America, and you might get an entire essay. o1 is built for depth, not speed.

And of course it can get the chain of thought wrong — like in this popular code cracking problem.

Where o1 shines

o1 opens up new possibilities in fields like science, coding, and advanced problem-solving.

OpenAI mentioned it’s especially useful for scientific research—like generating formulas for quantum physics or solving tricky coding challenges.

In coding competitions like Codeforces, o1 performed in the 89th percentile. That’s impressive, given these contests aren’t just about writing code—they demand serious problem-solving skills.

Even in everyday tasks, o1 proves its value when complexity is high.

I saw this interesting instance where someone asked o1 to help plan a Thanksgiving dinner for 11 people with only two ovens. The model didn’t just give a recipe; it created a detailed cooking schedule, even suggesting renting an extra oven to make the day easier. That’s the kind of thinking it brings to the table.

Worth the hype?

o1 won’t replace GPT-4 for most day-to-day tasks. It’s slower, pricier, and can be overkill for simple queries. But when you’ve got a problem that needs serious brainpower—whether it’s writing complex code or solving advanced math problems—o1 is unmatched.

It probably won’t be your go-to for everything, but it’s a powerful tool for those moments when you need something that thinks, not just computes.

So, what do you think? Is o1 something you’ll be using, or are you sticking with GPT-4 for now?

These coding fonts are incredible

Why do many devs keep using that boring Consolas font?

VS Code thinks it’s the best but there are so many better font options out there for you.

Here are 10 stunning fonts to spice up your coding game and upgrade your developer quality of life.

1. Fira Code

It’s extremely popular and for good reason.

This was where I first discovered the magic of ligatures.

Ligatures — those gorgeous character combos that naturalize your code.

It’s legible, open source and free to use.

Made by Mozilla (but hosted in the GitHub repo of some “tonsky” guy?)

2. Fantasque Sans Mono

An elegant font to bring out the joy in your workspace.

We used to call it Comic Sans Neue Mono because of the similarity to Comic Sans and Helvetica Neue. 

3. Roboto Mono

100% chance you’ve seen a Roboto font somewhere.

YouTube, Android, Material Design… this is Google’s baby.

So this is Roboto for coding.

You’ll see it all over the place in Google Dev Docs too — Android Dev, Firebase, GCP…

4. Anonymous Pro

Made specifically for coding with excellent readability — thanks to consistent spacing and clear character descriptions.

It reminds me of the Monospace font in that Notepad++ editor I used in the past (underwhelming compared to VS Code ofc).

5. Cascadia Code

Fira Code was great but I found the characters too tall.

So I switched to this beauty that I’ve been with ever since.

Supports ligatures too:

6. Noto Sans Mono

Clean and modern font that renders perfectly across multiple screen sizes and languages.

Open-sourced and free to use.

7. JetBrains Mono

The lovely font you’ll find in WebStorm, IntelliJ and every other IDE from Jetbrains.

8. Comic Neue

It’s a comic-inspired coding font with an energetic appearance.

It may give you a certain feeling of casualness in your coding (for better or worse).

9. Pixelify Sans

The chunky, pixelated letter forms give you the nostalgia of all those old video games from the past.

10. Victor Mono

Clean and minimalist with excellent readability and gracefully cursive italics.

There’s a font here for every developer’s taste – all clear and breathtaking to upgrade your coding experience.

Don’t waste your time reading docs

I was hopelessly pouring through coding books hoping for the knowledge to stick to my brain.

Reading and re-reading C++ books so I wouldn’t “forget” important string functions.

Memorizing huge swaths of random C# classes and methods (like seriously?)

But eventually I realized what a stupid waste of time this was.

I should never have paid so much attention to these random APIs and docs.

I should have focused on action.

I should have focused on general essential concepts instead of obsessing with specifics of ever-changing languages and frameworks.

Why waste so much time on API specifics when you can easily look it up on Google?

Most of the knowledge docs give only matter on a need-to-know basis.

When I need it, I will search for it and be on my way.

And eventually it will stick to my brain after I search and use it enough times — which is why action is so important — you’ll have far greater efficiency and retention when you learn by doing rather than memorizing random facts.

Look, I don’t really need to know how to loop through an array in PHP or JavaScript or whatever beforehand.

But I need to understand iteration as a coding construct and how I can use it to solve problems.

Problem solving. Isn’t that what computing is all about?

If you’re new to coding and learning say Python, your goal is NOT to become a Python expert.

Your goal is to learn how to think like a computer.

This isn’t about Python.

You learned about Python variables so you can use data storage to solve problems.

You’ll learn about Python arrays and dictionaries so you can structure data effectively to solve problems.

You’ll learn about if statements so you can make dynamic decisions for all the different scenarios a problem may bring.

So you see you learn the tool not for the tool’s sake but for why it exists in the first place.

Now you can effortlessly swap out one tool for another.

New languages can be learned on the fly: Dart, PHP, C#, whatever.

Even “weird” languages like Lisp only take a bit of familiarization to figure out:

I’m sure you can tell what this does if you’ve been coding for a while

When you look at a new problem your mind computerizes it easily.

What are the inputs and outputs? And what’s the best to represent them for efficient storage and retrieval?

What sequence of transformations are needed to act on the data and inputs to get our desired result?

What are the conditions that alter the pathways through the sequence?

How do we break the data and the transformations into more manageable units at different levels of abstraction?

All this you can do without even writing a single line of code.

And that’s the thing — coding isn’t the typing. The typing is actually more of a passive activity.

The real coding was the thinking you did by asking all the questions the above to design the solution.

Once the solution has been fleshed out, implementation is a no-brainer, whether it’s C++, PHP, Dart or JavaScript.

Stop writing code comments

Most comments are actually a sign of bad code.

In the vast majority of cases developers use comments to explain terribly written code desperately in need of refactor.

But good code should explain itself. It should tell the full story.

❌ Before:

You did too much in one go and you know it — so you drop in a bad comment to explain yourself:

JavaScript
// Check if user can watch video if ( !user.isBanned && user.pricing === 'premium' && user.isSubscribedTo(channel) ) { console.log('Playing video'); } // codingbeautydev.com

✅ After:

Now you take things step-by-step, creating a clear and descriptive variable before using it:

Comment gone.

JavaScript
const canUserWatchVideo = !user.isBanned && user.pricing === 'premium' && user.isSubscribedTo(channel); if (canUserWatchVideo) { console.log('Playing video'); } // codingbeautydev.com

You see now the variable is here mainly for readability purposes rather than storing data. It’s a cosmetic variable rather than a functional one.

✅ Or even better, you abstract the logic away into a function:

JavaScript
if (canUserWatchVideo(user, channel)) { console.log('Playing video'); } function canUserWatchVideo(user, channel) { return ( !user.isBanned && user.pricing === 'premium' && user.isSubscribedTo(channel) ); } // codingbeautydev.com

Or maybe it could have been in the class itself:

JavaScript
if (user.canWatchVideo(channel)) { console.log('Playing video'); } class User { canWatchVideo(channel) { return ( !this.isBanned && this.pricing === 'premium' && isSubscribedTo(channel) ); } } // codingbeautydev.com

Whichever one you choose, they all have one thing in common: breaking down complex code into descriptive, nameable, self-explanatory steps eradicating the need for comments.

When you write comments you defeat the point of having expressive, high-level languages. There is almost always a better way.

You give yourself something more to think about; you must update the comment whenever you update the code. You must make sure the comment and the code it refers to stay with each other throughout the lifetime of the codebase.

And what happens when you forget to do these? You bring unnecessary confusion to your future self and fellow developers.

Why not just let the code do all the talking? Let code be the single source of truth.

Your var names are terrible

❌ Before: Lazy variable naming so now you’re using comments to cover it up:

JavaScript
// Calculate volume using length, width, and height function calculate(x, y, z) { return x * y * z; } calculate(10, 20, 30); // codingbeautydev.com

✅ After: Self-explanatory variables:

JavaScript
function calculate(length, width, height) { return length * width * height; } calculate(10, 20, 30); // codingbeautydev.com

✅ Even better, you rename the function too:

JavaScript
function calculateVolume(length, width, height) { return length * width * height; } calculateVolume(10, 20, 30); // codingbeautydev.com

✅ And yet another upgrade: Named arguments

JavaScript
function calculateVolume({ length, width, height }) { return length * width * height; } calculateVolume({ length: 10, width: 20, height: 30 }); // codingbeautydev.com

Do you see how we’ve completely exterminated comments?

Like just imagine if the comment was still there:

JavaScript
// Calculate volume using length, width, and height function calculateVolume({ length, width, height }) { return length * width * height; }

You can see how pointless this comment is now?

Oh but yet again, we have developers writing tons of redundant comments just like that.

I think beginners are especially prone to this, as they’re still forming the “mental model” needed to intuitively understand raw code.

So they comment practically everything, giving us pseudocoded code:

JavaScript
// Initialize num to 1 let num = 1; // Print value of num to the console console.log(`num is ${num})`;

But after coding for a bit, the redundancy of this becomes clear and laughable, worthy of r/ProgrammerHumor.

Delete commented out code

They’re ugly and wishy-washy.

I can’t STAND them!

You did it because you were scared you’ll need it in the future and have to start all over from scratch.

You should have used Git.

Okay you’re already using Git — well you shouldn’t have written such terrible commit messages. You should have committed regularly.

You knew that if you deleted the code, it’ll be hell to go through your vaguely written commits to find where it last existed in the codebase.

Comments worth writing?

1. TODO comments

Perfect for code tasks in highly specific part of the codebase.

Instead of creating a project task saying: “come up with something better than the for loop in counter/index.js line 2”

You just add a TODO comment there — all the context is already there

2. Public APIs

One reason people love Flutter is the amazingly extensive documentation — both online and offline:

These all came from the massive amount of comments in the codebase:

3. Why did I do this?

Sometimes you need to explain why code is a certain way from a bigger-picture perspective.

Like a warning:

JavaScript
// Keep it at 10 or else the server will crash in v18.6.0 const param = 10;

Or check out this from React’s source code:

Yeah it’s pretty damn tough to put that gigantic explanation into code form. Here comments do make sense.

Final thoughts

Always look for ways to show intent directly in code before pulling out those forward slashes.

Comments give you something else to think about and in most cases you actually need a refactoring.

Let code lead.

Why does [] == ![] return TRUE in JavaScript?

It’s hard to believe.

How can an Array not be an Array?

It makes no sense — [] is truthy and ![] should be false.

So how can [] equal false?

And this somehow doesn’t happen for other types like strings and numbers:

Are JavaScript arrays broken?

What happened here

Dump all the blame on the dangerous == operator.

This is just another instance of why we always tell new JavaScript devs to NEVER use it (ever).

Especially if they’ve already been coding with stubbornly strict languages like C#.

At first glance, it doesn’t seem like there’s anything wrong with ==:

Plain text
// C# int num = 2; Console.WriteLine(num == 10); // false Console.WriteLine(num == 2); // true
JavaScript
// JS let num = 2; console.log(num == 10); // false console.log(num == 2); // true

But now look what happens here:

Plain text
// C# int num = 2; // ❌ Error: can't compare int and string Console.WriteLine(num == "2"); // false

But look what happens in JavaScript:

JavaScript
// JS let num = 2; console.log(num == "2"); // true?

JavaScript auto-casts the string into a number!

This is one of the many frustrations people have with JavaScript that made TypeScript come along.

Instead of just failing and stopping us from doing dumb stuff, it just goes “Mhmm okay, if you say so…”

JavaScript
// Huh? console.log(null == undefined); // true // Converts array to string console.log([1,2,3] == "1,2,3"); // true (seriously?)

So what do you think REALLY happens in [] == ![], behind the scenes?

First of all, empty arrays are truthy in JavaScript, so ! acts on it to make it false

JavaScript
[] == false

So now surprise surprise, we suddenly find ourselves comparing an Array to Boolean. Obviously not gonna end well.

As we now know, JS doesn’t care so it just goes ahead — this time casting the Boolean to the equivalent number

JavaScript
[] == Number(false) [] == 0

Next, thanks to some garbage rules that you don’t need to ever know, [] turns into… an empty string?

JavaScript
// don't ask "" == 0

And now finally it converts "" into… a Number:

JavaScript
0 == 0 // true

So what’s the solution to avoid this nonsense?

Always use the strict equality operator === (and I mean always).

JavaScript
// font ligatures make triple equal look nicer console.log(2 == '2'); // ❌ true console.log(2 === '2'); // ✅ false console.log([] == ![]); // ❌ true console.log([] === ![]); // ✅ false

There is no scenario imaginable in the universe where == can be used that === can’t be

And now with ===, your lovely VS Code editor suddenly comes alive to stop us from doing something like this:

It still compiles though — unless you use TypeScript

But before it was asleep:

But what about [] == []?

Okay this makes sense, but what then could possibly explain this:

Surely the == can’t be blamed here. They have the same type, don’t they?

Yes they do.

The problem is that JavaScript compares arrays by reference. Not by value.

They may have exactly the same value, but as long they don’t refer the same object in memory, they will never be equal in the eyes of == and ===

JavaScript
// these are creating new array objects // on the fly, stored at different locations console.log([] == []); // false console.log([] === []); // false // store new object this time const arr = []; // reuse it const tari = arr; // both vars refer to the same object in memory console.log(arr == tari); // true console.log(arr === tari); // true

And this is how it is for objects in general too:

JavaScript
console.log({} === {}); // false const person1 = { name: 'tari ibaba' }; const person2 = { name: 'tari ibaba' }; const person3 = person1; // Only person1 and person3 point to the same object console.log(person1 === person2); // false console.log(person1 === person3); // true console.log(person2 === person3); // false

But of course, this isn’t the case for our core primitive values — strings, numbers, and booleans:

JavaScript
console.log('tari ibaba' === 'tari ibaba'); // As we saw previously console.log(2 === 2);

So what do you do when you want to compare arrays by their element values?

If it’s sorted you can use JSON.stringify():

JavaScript
function arraysEqual(array1, array2) { return JSON.stringify(array1) === JSON.stringify(array2); }

Otherwise, you go for the more generic length and every() combo:

JavaScript
function arraysEqual(array1, array2) { return ( array1.length === array2.length && array1.every((value, index) => value === array2[index]) ); }

Final thoughts

== is just one example of JavaScript’s looseness making it do things that make no sense in the real world.

Moral lesson: Always use strict equality, use TypeScript, and prioritize modern features.

How do computers actually understand the code you write?

How do computers actually understand the code we write?

It’s interesting how some people code for several years without ever learning these essentials. And often take it for granted.

How can a simple text command somehow control millions of screen pixels in a specific area with such incredible precision?

We take it for granted, but how does this soulless machine “know” that a random “console.log()” text means it should change the specific pixels at that point in the screen to match the string in the brackets?

How can text trigger interactions with systems and servers thousands of miles away in the blink of an eye, using raw electricity?

Let’s dive into a fundamental overview of how it all works.

Why coding matters

You see at their core, computers are nothing but a gigantic network of complex interconnected circuits.

Everything your computer does comes from having electric current flow through the circuit.

The core of computing is using these currents as vessels for real-world data.

In digital computing, there are only two states of current: On (1) or Off (0). Just like in a light switch.

We use these two states to pass messages (instructions) to the complex circuit (processor/CPU).

Because of the brilliant way we design the circuit, passing different instructions to the processor makes it “do” different things (a whole other subject on its own)

In a 1-bit processor, you only have 2 possible instructions — 1 or 0 (binary).

But to make a full-fledged computer we need room for much more than two instructions.

That’s why in practice, we use batches of 1s and 0s to represent as many instructions as we need.

Plain text
Possible instructions 1-bit processor: 1 and 0 2-bit processor: 11, 10, 01, 00 n-bit processor: 2^n possible instructions

We can represent them with a string of 1s and 0s. Or with hexadecimal numbers. Or with more human-friendly notation.

Plain text
// They're all the same instruction // These represent batches of electric signals in the real-world 1011100000000001000000000000000000000000 // Hex form B8 01 00 00 00 // Human-friendly -- Assembly language MOV EAX, 1

An instruction is like the smallest indivisible unit of any abstract action your computer can take — an atomic action.

On their own, they do incredibly basic things — adding binary numbers, moving current state from one part of the circuit to another, etc.

But the real power of computing comes when processors execute a massive amount of instructions together (millions and billions).

Luckily this isn’t a problem today as we have household processors of up to 3 GHz today — processing 3 billion instructions in a second (!).

When we code, we combine these instructions in unique ways to make amazing things happen.

Text to 1s and 0s

You could write a program by passing the electric currents directly to the processor as instructions.

You wouldn’t need any operating system or input device.

But unfortunately, you’d need sequences of thousands and millions of instructions to do anything meaningful with your computer.

It will take you several weeks and months to do something as simple as displaying a bunch of characters on the screen (like this).

That’s why we created expressive languages that could do in one line what takes dozens or hundreds of machine instructions.

Then we created programs to convert from those expressive languages to Assembly language and eventually to the machine instructions (a whole other subject on its own)

Programs that we call compilers.

Unlike our normal human languages, these expressive languages are incredibly precise with zero room for ambiguity.

So with compilers, we go from [C++] code like this:

JavaScript
int a = 2; int b = 3; int sum = a + b;

To Assembly code like this:

JavaScript
a: .long 2 b: .long 3 sum: .zero 4 __static_initialization_and_destruction_0(): push rbp mov rbp, rsp mov edx, DWORD PTR a[rip] mov eax, DWORD PTR b[rip] add eax, edx mov DWORD PTR sum[rip], eax nop pop rbp ret _GLOBAL__sub_I_a: push rbp mov rbp, rsp call __static_initialization_and_destruction_0() pop rbp ret

And eventually to the set of machine instructions — what we all call programs or apps.

But the CPU doesn’t run this machine code directly.

The generated machine code is different for every operating system — that’s why .exe files can only run on Windows, and .apk can only run on Android.

When the program runs, it’s the OS that sends the actual low-level instructions for the specific processor, according to whatever is in the program.

When you connect this processor to external devices like network adapters, speakers, monitors, and more, these instructions can transmit specialized signals to these peripherals, and incredible things happen.

Final thoughts

The magic lies in the unseen dance of circuits and logic.

Each line we write sparks a journey from human intent to digital action, as electricity interprets our commands through the language of 1s and 0s.

This profound synergy transforms our abstract ideas into a tangible, interactive digital realm, revealing the intricate beauty of computing’s core.

The 5 most transformative JavaScript features from ES12

ES12 was truly an amazing upgrade.

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. Promise.any()

Before ES12, we already had Promise.all() and Promise.allSettled() to wait for an entire group of Promises.

There were several times when we’d have several Promises but only be interested in whichever one resolved first.

So Promise.any() had to come into the picture:

JavaScript
async function getHelpQuickly() { const response = await Promise.any([ cautiousHelper(), kindHelper(), wickedHelper(), ]); console.log(response); // Of course! } async function cautiousHelper() { await new Promise((resolve) => { setTimeout(() => { resolve('Uum, oohkaay?'); }, 2000); }); } async function kindHelper() { return 'Of course!'; } function wickedHelper() { return Promise.reject('Never, ha ha ha!!!'); } // codingbeautydev.com

One interesting thing to note: even though any() resolves immediately, the app doesn’t end until all the Promises have resolved.

2. replaceAll()

Yes we already had replace() for quickly replace a substring within a string.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible!!'; const result = str.replace('terrible', 'wonderful'); console.log(result); // JavaScript is so wonderful, it is unbelievably terrible!! // Huh? // codingbeautydev.com

But it only did so for the first occurrence of the substring unless you use a regex.

So ES12 gave us now we have replaceAll() to replace every single instance of that substring.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible.'; const result = str.replaceAll('terrible', 'wonderful'); console.log(result); // JavaScript is wonderful, it is unbelievably wonderful. // Now you're making sense! // codingbeautydev.com

3. WeakRefs

As from ES12, a JavaScript variable can either be a strong reference or a weak reference.

What are these?

The Strong Refs are our normal everyday variables. But the Weak Refs need to be explicitly created with WeakRef():

JavaScript
const strongRef = { name: 'Tari Ibaba' } const weakRef = new WeakRef(strongRef); // codingbeautydev.com

JS variables are just references to the actual object in memory.

That’s why we can have multiple references to the same object.

JavaScript
const person = { name: 'Tari Ibaba' }; const me = person; // modifies the actual object that `me` points to person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com

But what’s the difference between a strong ref and a weak ref?

Well in programming we have something garbage collection, which is when unneeded objects are removed from memory to save resources.

In JavaScript, objects are automatically marked for garbage collected when all the strong ref variables pointing to it have become unreachable — out of scope:

The object person and me both point to is put on the destruction queue once func() runs.

JavaScript
func(); // 💡`person` and `me` are unreachable here function func() { // 💡this object will be marked for garbage collection // after this function runs const person = { name: 'Tari Ibaba' }; const me = person; person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com }

But look what happens here:

Even person went out of scope after func() finished, we still had me, a global strong reference.

JavaScript
let me; func(); // 💡one strong reference (`me`) is still reachable // ✅ Can always access object console.log(me.site); function func() { // 💡this object will NOT be garbage collected // after this function runs const person = { name: 'Tari Ibaba' }; me = person; person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com }

But what if me was a weak reference?

Now after func() executes, person would be the only strong reference to the object.

So the object will be marked for garbage collection:

JavaScript
let me; func(); // No strong references reachable // ❌ Bad idea: object may not exist console.log(me.deref().site); function func() { // 💡this object will be marked for garbage collection const person = { name: 'Tari Ibaba' }; me = new WeakRef(person); person.site = 'codingbeautydev.com'; console.log(me.deref().site); // codingbeautydev.com }

So why do we need weak references?

The biggest use case for them is caching.

Look what happens here: processData() runs and we have a new object stored in our cache.

Even though data becomes unreachable, the object will never be garbage collected because it has a strong reference in the cache.

JavaScript
let cache = new Map(); processData(); function processData() { const url = 'api.tariibaba.com'; const data = fetchData(url); // process data for app... } async function fetchData(url) { // check cache const saved = cache.get(url); if (!saved) { const data = await (await fetch(url)).json(); cache.set(url, data); } return saved; }

But what if I want the object to be freed up after processData() exits?

I would use a WeakRef as the Map values instead:

JavaScript
let cache = new Map(); processData(); function processData() { const url = 'api.tariibaba.com'; const data = fetchData(url); // process data for app... // 💡the object will only exist in cache // in this function } async function fetchData(url) { // deref weak ref const saved = cache.get(url).deref(); if (!saved) { const data = await (await fetch(url)).json(); // ✅ Use a WeakRef instead cache.set(url, new WeakRef(data)); } return saved; }

4. Logical assignment operators

Lovely syntactic sugar from ES12.

We use them like this:

JavaScript
left ??= right; left ||= right; left &&= right; // codingbeautydev.com

Exactly the same as:

JavaScript
left = (left ?? right); left = (left || right); left = (left && right); // codingbeautydev.com

??=. Quickly assign a value to a variable *if* it is null or undefined (“nullish”).

JavaScript
user.preferredName ??= generateDumbUserName();

||=. Like ??=, but assigns the value for any falsy value (0undefinednull''NaN, or false).

JavaScript
user.profilePicture ||= "/angry-stranger.png";

And then &&=. Something like the reverse; only assigns when the value is truthy (not falsy).

JavaScript
user.favoriteLanguage = await setFavoriteLanguage(input.value); user.favoriteLanguage &&= 'Assembly'; // You're lying! It's Assembly!

5. Numeric separators

Tiny but impactful new addition that made big number literals more readable and human-friendly:

JavaScript
const isItPi = 3.1_415_926_535; const isItAvagadro = 602_214_076_000_000_000_000_000; const isItPlanck = 6.626_070_15e-34; const isItG = 6.674_30e-11; // Works for other number bases too... // codingbeautydev.com

The compiler completely ignores those pesky underscores — they’re all for you, the human!

Final thoughts

These are the juicy new JavaScript features that arrived in the ES12.

Use them to boost your productivity as a developer and write cleaner code with greater conciseness, expressiveness and clarity.