DEV Community

Bruno Gelb
Bruno Gelb

Posted on • Updated on

What have I learned in a month of JavaSript development as a Python developer

table of contents

  • intro
  • js
  • html
  • css
  • special thx to

intro

For the last few years I was a python-terraform-ansible-gcp engineer. Regardless, my latest quest was to build a PoC (proof of concept) — and that's including UIUX part.

Considering I was super rusty with JS / HTML / CSS (last time I've touched these Pluto was still a planet and that's sayin') this was a promising challenge from the start, but I've never anticipated how much pain I'll actually find..

So I want to share some outcome thoughts with you ladies and gentlemen just to feel sane again. Oh and also to be of some help to backend developers / JS apprentices.

disclaimer: These lessons-learned are not something that should be used per se for decisions in commercial projects! I'm not a professional frontend developer!

js

  • as a rule of thumb always aim for the native & modern solution. Basically yeah, this is true for most languages, however JS is the place where the profits of that are super noticeable, almost palpable. Most of the time popular JS-related questions at stackoverflow usually contain 3 types of solutions: jquery, es6/es7 and the old js. You need the es6/es7 one. You'll easily spot that one since it'd be the most laconic.

  • const -> let -> var (for variables and functions declaration). Which means: const is the best choice, the second best thing is let, making var the worst.

  • template literals are literally awesome, use them everywhere to format your strings (instead of string concatenation):

let name = 'Bruno';
console.log(`Hello ${name}, sum of 2 and 3 is ${2+3}`);

// Hello Bruno, sum of 2 and 3 is 5
Enter fullscreen mode Exit fullscreen mode
  • frameworks (react.js, vue.js) are actually hard. Even the ones that are in fact human-faced. You'd have to learn state, components, initialization, bundling, .. BEFORE anything will work for you. I have exceeded the time limit I was comfortable with to put on that and therefore had to drop vue.js.

  • w/o framework you don't have a way to render your templates at the browser side. Which actually leads to ugly js code. I don't think I have a proper solution for that. I just left it as an ugly js code building html pieces. It works.

  • beware of red-blue functions and async code. The thing is, other languages with red-blue functions (like Python) generally allow you live in a sync world by default and (whenever you ready) you are ok to start doing some async work.
    This is not the case with JS. It's async by default, and (though there are decent reasons for that) it damages your learning curve badly. The code seems to be working properly but with the first user you'll start to get mandelbugs, lots of them — because your flow of execution is unpredictable, and that's because you have a terrible mess of a sync and an async code due to the inexplicit syntax.
    I'm confused what advice to give here, but what helped me was to carefully check where are actually async code and what pieces / in which order call it. At the end I've started to use chained promises for the async and it worked like a charm.

  • please consider allocating some men-hours / women-hours in order to build & bundle your project. In my case in a temptation to cut corners during a small project I've decided to go w/o it and was punished multiple times. I couldn't use Sentry, Typescript, some of the linters, many libraries and widgets — because in modern world everyone simply assumes you're building & bundling your js, so they won't provide you with <script> way to plug-in.

  • typescript is an awesome way to solve a lot, but not most of the js pitfalls. Reach for it early, it's totally worth it

  • while I've mentioned typescript, objects comparison / checking for null|undefined|empty string are indeed a huge pain. (Yeah turns out it wasn't just a silly pub joke Python programmers tell each other.) Don't even try to guess it. stackoverflow / mozilla docs are your only friends on this damned land

  • definitely setup an auto-formatter (in js the go-to weapon is prettier, and the best thing is that you could use it even without CI/CD simply via pre-commit hook) and preferably also a linter (I've tried eslint, and it gave me nothing useful but a lot of false positives. Maybe the default configuration was off, no idea).

  • the other main danger of the js: the language silently swallows any errors and issues that'd happen during execution flow. Yes, that's exactly as bad as it sounds, because due to that fundamental design flaw you never actually know what happened and when. (Again, I know it was intentional, still it's terrible in terms of developer experience.)
    I've had some success fighting this by introducing extensive logging everywhere across my code, it looks kinda like this:

function debug(msg) {
    const today = new Date();
    const time = `${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`;
    const js_module = import.meta.url.split('/').pop();
    console.log(`[${time}] [${js_module}] [DEBUG] ${msg}`);
}

function handleMyEvent(e) {
    debug(`${e.type} handler invoked`);
    // logic
}
Enter fullscreen mode Exit fullscreen mode

again, ain't pretty but it works well. The import.meta.url is the chef's kiss I like: it shows you which js file actually throwed the log message.

  • avoid window.onload / document.onload statements: they will silently override each other if you're using them in more than one file. Prefer constructions like this instead:
function main() {
    // your logic
}
window.addEventListener('load', main, false);
Enter fullscreen mode Exit fullscreen mode
  • avoid working with localStorage directly. It seems friendly and handy, but that's deceptive: native js localstorage can only do strings, and (for example) it will gladly and silently accept empty array from you, but it will return empty string back on that key. Use 3rd party wrappers instead (I've chosen lockr and I'm pretty happy with it) or methods of your framework (that one is preferable if you have a framework)

  • beware of caching: opposed to the backend world where nothing is cached untill you configure it to do so, in the frontend world EVERYTHING is cached by default. And that's not only static server, it's also cdn, local server, browser and so on. You'd have to spent a bit of time till you'll learn each layer and how to work with them. I'd emphasize on one though: caching of static files on redeploys.

  • beware of mutation. Luckily I havn't cached one of those issues but it looks scary enough to be aware of.

  • don't you ever fall into the temptation of implementing your filters like I did.
    What I did was: at each user click query all checkboxes, put all the gathered states into localStorage, then just throw a window.location.reload(); and on each page load use localStorage value to render controls.
    Turns out (surprise!) not only this logic is super bad for the page performance / user experience, but it's also unreliable as hell (data flow between localStorage and controls is unpredictable) and leads to the ocean of non-debuggable bugs.
    Instead use CustomEvents, separate listeners and dispatchers by js modules properly ― this specifically is how I rewrote it and it worked like a charm ever since.

html

  • definetely start by throwing in additional meta-tag in your base template:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
Enter fullscreen mode Exit fullscreen mode

this'll allow users to somewhat decently access your site from mobile

  • aim really hard to NOT use <div> tag for everything. There are <section>, <a>, <main> and many others. This is the important concept called "semantic html", it's easy and it greatly helps both to read the code and to access the site (a11y).

  • whether possible try to link your js files as modules, not as scripts. That way you'd be able to reuse code from them:

<script type="module" src="{% static "js/sidebar.js" %}"></script>
Enter fullscreen mode Exit fullscreen mode

css

  • always use classes, never ids. That's much more reliable and maintainable (for instance, it covers the case with multiple elements that carry the same purpose). Then you could access it with a selector like this:
document.querySelector('.toggle_button'); // for a class toggle_button
Enter fullscreen mode Exit fullscreen mode
  • if you're assigning more than one css class to an element via literal, don't forget you have to do it in a separate class statement:
<button class="button_yellow button_toggle"></button> // correct

<button class="button_yellow" class="button_toggle"></button> // wrong
Enter fullscreen mode Exit fullscreen mode

otherwise one of the two class="" instructions will silently override the other.

  • assign different classes with meaningful names to elements with different purposes, don't cut corners on naming in css

  • I've had moderate success by using either display: flex; or display: inline-block; on css rules in order to make page blocks behave, but besides that css is still some sort of weird witchcraft and deserves it's own article.
    However this small educational material by Julia Evans is the best thing I could only think of on the subject.

special thx to

Discussion (1)

Collapse
twanttobealighi profile image
Daria Moreno-Gogoleva

i guess you've done pretty well

thanks for sharing this experience ✨