Tagged: javascript Toggle Comment Threads | Keyboard Shortcuts

  • kmitov 7:37 am on February 19, 2021 Permalink |
    Tags: javascript, typescript   

    “In one paragraph or less” – why I chose JavaScript over TypeScript 

    My idea with this article is to try to summarize and share “in one paragraph or less” the main reason why, as a CTO, I chose one technology over another. We take everything into account, infrastructure, team, business requirements, and many others, which could be quite complex, but can we share the essence.

    In one paragraph or less

    “I chose vanilla JavaScript compiled with Google Closure Compiler and not TypeScript because I was building an extensible plugin based framework and I wanted to allow each and every plugin developer to be free to choose vanilla JavaScript or TypeScript for their plugins. I did not want to limit anyone’s future choices.”

    The pre-story (if you are interested)

    A few days ago I called an old friend. We’ve been acting like CTOs of two companies for a couple of years (I was more acting, he was doing like the real deal). But we haven’t talked in a while. The conversation went pretty fast from “How are things at work and at home?” to “Why did you choose this technology over that technology?”

    He: – We made some interesting things on the technology front.

    Me: – Really? What?

    He: – We went for the React, TypeScript path and did…

    Me: – When I had to make this decision I stayed on the Rails, Stimulus with vanilla JS path.

    He: – You know, if it wasn’t for this and that, I would have done as you did. But you should definitely check GraphQL in more detail.

    Me: – Oh, I have and …

    This conversation went on for some time.

    I had a very similar conversions about an year ago with another CTO friend that said:

    I chose “technology X” instead of “technology Y” to keep some sanity in our team.

    So we make these decisions, but can we communicate them?

     
  • kmitov 6:39 pm on February 15, 2021 Permalink |
    Tags: javascript,   

    “if statements” are a “code smell” 

    (Everyday Code – instead of keeping our knowledge in a README.md let’s share it with the internet)

    Today we made a code review on a feature. Generally the review process in our team is – someone drops a message in the slack channel and says – “hey, I have these changes. Can you please take a look at them?”. We do not require a merge from an “authority” to get something to production. We also have one rule we are trying to follow:

    Your commit should make the product better than it previously was.

    https://www.axlessoft.com/careers/

    The commit below did not make the code better and this is the article about it. I am sure it would be useful for all of our team members and I hope it will be useful for the community as a whole.

    How “if statements” are a sign of “code smell”

    This is the commit. Do you see the problem with it?

       /**
        * @private
        * @param      {IS.StepsTree.LoadedEvent} event
        */
       onStepsTreeLoaded(event) {
    -    this._buildId = this.generateBuildId();
    +    if (!this._modeChangeOccured) {
    +      this._buildId = this.generateBuildId();
    +    } else {
    +      this._modeChangeOccured = false;
    +    }
    +
    

    The logic in the onStepsTreeLoaded method has significantly changed. It was a simple initialization of a private variable. Now it is an if statement with an else that sets the variable used in the if to a false value.

    Wow. This is even difficult to explain.

    Why was the change introduced and how the “code smell” helped us improve?

    The thing is that our colleague had to do this change to keep the behavior of the code based on a commit from 7 months ago. But now we see this smelly code and we thought:

    Why is this even needed in the first place? Why do we call this onStepsTreeLoaded method and what is it doing for us?

    Turns out that we can just move the initialization from onStepsTreeLoaded method to another method called at a different place and we can delete this method. We would keep the same behavior. There will be no regressions. The framework has changed in the last few months to the point that there is now a better place for this initialization to happen.

    My point is: “if statement”==”code smell”

    Adding an if statement to a working code is probably a code smell. Wrapping an existing code in an if statement based on unrelated state with an else that sets this same state is probably the precise definition of what “code smell” is.

    Conclusion

    Revise your assumptions. Don’t add the if statements. Think again if this is really needed, why it is needed and where is its place in the architecture of the platform.

    The concerned emotion

    Can’t think of a better way to show you the emotion of code smell than to show you this Gorilla.

    FabBRIX WWF, Gorilla in 3D building instructions
     
  • kmitov 10:25 am on January 22, 2021 Permalink |
    Tags: , , Google Closure Compiler, javascript, ,   

    Quality in an event-driven plugin based browser framework. 

    (Everyday Code – instead of keeping our knowledge in a README.md let’s share it with the internet)

    We’ve designed, developed from scratch and are running an event-driven plugin based browser framework that we call Instructions Steps (IS). It helps us visualize 3D models and building instructions on the BuildIn3D and the FLLCasts platforms. Currently it consists of hundreds of extensions separated in about ~50 repos with 587 releases. We’ve figured out a way to keep the quality of the whole framework at a really good level with almost no bugs and errors.

    This article is about how we are doing. The main purpose is to give an overview for newcomers to our team, but I hope the developers community could benefit from it as a whole.

    The IS architecture (for context)

    I will enter into the details about the architecture of IS in another article. For this article it is enough to say that IS consists of a really small core – 804 lines of code and a lot of extensions.

    There are many extension that are extending the framework. Most of them are under 200 lines. The framework is highly decoupled and “everything is an extension”. Look at the 3D model and building instruction below. The “previous” button is an extension. The “next” button is an extension. You could have “parts list”, “bill of materials”, play animations, fit and rotate the camera. These are all extensions. I took this idea for the way we were building plugins for Eclipse (many years ago).

    Sphere from Geosmart GeoSphere, but this time in 3D

    What is the problem with quality and how do we keep delivering a quality product?

    Event-driven plugin based architectures have many advantages – like decoupling the plugins which makes them more maintainable. It forces you to have clear API boundaries which also makes them more maintainable. But there are a lot of questions and drawbacks compared to a nice Monolith app. What should we test? Should we test a specific extension, or the repo or the extension as it is working with all of its direct dependencies. What kind of specs should we develop? Should we have small unit spec that tests the extension in an isolation or we should put all the hundreds of extensions and test them all together. How do we make make these decisions?

    Here are the few simple rules that we try to follow.

    Compilation and type checking with Google Closure Compiler in ADVANCE mode

    We use vanilla JavaScript. No TypeScript. There are reasons for this. Nothing against TypeScript actually. We use Google Closure Compiler to compile each and every extension.

    Here is an example of a declaration of an “interface method”

    /**
     * Loads the given url and returns a Promise that when resolved will provide the caller with a {@link IS.StepsTree.StepData}.
     *
     * @export
     * @param  {string|File} file - url to the file or a DOM File object to be loaded
     * @return {Promise} Promise that when resolved will provide a {@link IS.StepsTree.StepData} which is the root step
     */
    IS.StepsTree.IProvider.prototype.getStepsTree = function(file) {};

    Looking at the code we have the jsdoc annotations like “@param”, “@return”, “@export”. These are annotations that GCC understands and will check. It will check if the param is of the given type, it will check if the returned value is of the given type. It will check if the classes that implement this interface actually implement it.

    Google Closure Compiler (GCC) will check if we are trying to access properties and methods that are not available.

    As a general rule of thumb – compilers are strict. If they understand your code, and compile it, then your code fulfills a bare minimum of requirements.

    GCC has helped us a lot. It takes some time to get used to it and to learn all the annotations and how to use them and how to develop SDK and libraries that are compiled, but it pays off. I’ve previously shared about our experience with GCC. Here is one lecture that I gave a few times – https://github.com/thebravoman/google-closure-compiler-presentation/blob/main/gcc_presentation.md

    Each extension is tested in isolation only with its direct dependencies available

    The navigation extensions are located in the repo “is-navigation”. When we test the functionality of the “Next” button we don’t expect to also have the “Fullscreen” or the “Animations” extensions available.

    Each extension is tested automatically in isolation, because each extension should work on its own given that it is the only extension that is installed (and the direct dependencies of course). Which makes sense. We are building a framework, a platform. When we have a framework, platform or even OS we should be able to install one extension, app, or program and they should be able to work on their own.

    For testing the extensions we use Jasmine and Teaspoon and I wrote an article about how and why we do it.

    All extensions are tested together in the ‘release_pack’

    What teams building platforms and frameworks quickly find out is that all the extensions and apps can work separately, but there are a lot of cases where if you put them all together and install them, things start to get more difficult. An example are all the different problems different OS have. One program is affecting another program in an unpredictable way.

    So we’ve build the is-release_pack. What it does is to put all the extensions together and to run a few basic tests on all of them.

    It contains 1-2 specs that check that each extension is working in the general case and probably one or two very specific cases. All the other specific cases are tested in the extensions, not in the “release_pack”. We push everything we can to the specs of the specific extension, but we have a few “integration” specs that are in the is-release_pack. And it is beautiful.

    The downside of integration specs

    There is one major downside with integration specs:

    All of us, developers, are lazy when it comes to really building it right. Once we build the feature and we see that it is working after a day of work there is little motivation in us to spend the next 3 days on building it right. It just feels so go to have it working that you commit and move on to the next thing.

    When there is a problem and an integration spec is failing most of the time it is easier to go and “ease” the integration spec. Change the expects a bit. Modify them. Even remove them.

    Other times when we have to develop a specific spec for the specific extension it feels easier to develop an integration spec instead of 20 specific specs in the repo for the extension.

    Sooner or later you end up in one of these situations:

    1. There are no integration specs or they contain expects and assertions that can not validate that your product is working correctly.
    2. There are a ton of integration specs that are constantly and randomly failing from time to time. The “integration” specs suite also takes forever to pass as there are now so many “integration specs”.

    Both of this situations are highly undesirable.

    Resolving the downside of integration specs

    One thing I learn writing business plans when applying for different VC funding is RACI.

    There are people Responsible for the job, people Accountable for the job, people that could be Consulted and people that should be kept Informed.

    So who is Accountable for the delivery of the IS framework and for the framework working correctly with all extensions in the user browser?

    Ideally it should be one person. In our case – it is Me.

    We are all responsible for the implementation. But in a team one should be held Accountable if something is not working and not right. One is Accountable for not checking. This person could change of course, but at any given moment there is someone “starting the engine of the car” as it exists the factory. You should start the engine and make sure the car works. You are accountable for checking it. You might not be responsible if it does not start, but you are accountable for checking.

    With the is-release_pack we resolved this for us.

    Only the Accountable (Me in our case) has access to the is-release_pack and its specs. Nobody else. You can not add integration specs, you can not remove, you can not even change on your own. The person that is accountable should do it. We keep the number of specs to a mininum – one basic scenario for each extension and when appropriate 1-2 (but no more) very specific scenarios for each extension. In the release_pack we prefer to has scenarios that involve more than one extension. In fact if there is a scenario that involves all the extensions we would probably use it.

    Integration specs are coupling the extensions?

    Yes. They are. When one extension fails the integration spec for all the extensions fail. That is true. With hundreds of extensions if every day a different extension “fails” then you will not have a successful run of the integration suite in years.

    But the customer “does not care”. The integration spec is the closes spec to the customer experience. The users never interacts with a single extension. They interact with all extensions.

    In the same time if an extension has reached the release_pack and is failing the release pack , we will go and add a new spec, but not in the release_pack. We add it into the repo for the specific extension. This protects us from regressions.

    Conclusion

    By having a small subset of integration specs in a project to which only I have access and that is the final step in the release pipeline we’ve managed to stop hundreds of releases that would break existing clients, would lose a feature or introduce a bug.

    587 official releases already and it takes 5 to 10 minutes to release the whole framework. Integration specs are present in the release_pack, but we keep them to a minimum, each testing many extensions at once and making sure that a real life client scenario is working.

     
  • kmitov 7:54 am on January 21, 2021 Permalink |
    Tags: , , jasmine, javascript, ,   

    How and why we test JavaScript – Jasmine and Teaspoon 

    (Everyday Code – instead of keeping our knowledge in a README.md let’s share it with the internet)

    We are serious about specs. A good suite of specs has helped us many times especially when it comes to regressions. For the JavaScript part of our stack many of our specs and using Jasmine and ran with Teaspoon in the Browser environment. This article is about how we use Teaspoon and Jasmine to run our specs for pure JavaScript frameworks like Instructions Steps, and BABYLON.js. Both BuildIn3D and FLLCasts are powered by this specs, but we use them mostly for the Instructions Steps Framework.

    Why JavaScript specs?

    In the Rails community specs are important. In the JavaScript community – well, they are not that well respected. We can debate the reasons, but this is our observation.

    When we implement specs for the platforms we like to use Rails system specs. They give us exactly what we need. Let’s take a real life example.

    Filtering by category

    We visit the page at BuildIn3D for all the 3D models & building instructions. We filter them by brand. We click on a brand and see only the 3D models & instructions for this specific brand.

    Here is the spec. Short and sweet.

    scenario "/instructions shows the instructions and can be filted by category", js: true do
      # go to the third page as the materials are surely not there
      visit "/instructions?page=3" 
      expect(page).not_to have_link material1.title
    
      click_category category1
    
      # We make sure the url is correct
      expect(page).to have_current_path(/\/instructions\?in_categories%5B%5D=#{category1.id}$/, url: true)
    
      # We make sure that only material1 is shown and material2 is not shown
      # The materials are filtered by category
      expect(page).to have_link material1.title
      expect(page).not_to have_link material2.title
    endT

    The spec is a Rails system spec. Other articles enter into more details about them. The point is:

    With a Rails system spec we don’t concern ourselves with JavaScript.

    We visit the page, we click, we see that something has changed on the page – like new materials were shown and others were hidden.

    What’s the use case for JavaScript specs then?

    Take a look at the following embedded BuildIn3D instruction. It is coming live and it has a next button. Click on the next button.

    TRS the Turning Radio Satellite construction from GeoSmart and in 3D

    Here is the actual spec in JavaScript with Jasmine and ran everyday with Teaspoon.

    it("right arrow button triggers TriedMoveIterator with 1", function(done) {
        // This is a JavaScript spec that is inside the browser. It has access to all the APIs 
        // of the browser. 
        const eventRight = new KeyboardEvent("keydown", { key: "ArrowRight" });
        document.onkeydown(eventRight);
        
        // Wait for a specific event to occur in the Instructions Steps (IS) framework
        // We are inside the browser here. There is no communication with the server
    
        IS.EventsUtil.WaitFor(() => this.iteratorListener.event).then(() => {
          expect(this.iteratorListener.event.getSteps()).toEqual(1);
          done();
        });
      });
    

    This the how we use JavaScript and this is why we need them:

    We need JavaScript specs that are run inside the browser to communicate with other JavaScript objects and the browser APIs for JavaScript apps.

    In this specific case there is this “iteratorListener” that monitors how the user follows the instructions. We need access to it. Otherwise it gets quite difficult to test. We can not write a spec to test what are the pixels on the screen that are drawn after clicking next. This will be … difficult to put it mildly. We don’t have to do it. We need to know that clicking the next button has triggered the proper action which will then draw the actual Geometry and Colors on the screen.

    How we use Jasmine and Teaspoon to run the JavaScript specs

    Years ago I found a tool called Teaspoon. Looking back this has been one of the most useful tools we’ve used in our stack. It allows us to have a Rails project (and we have a lot of those) and to run JavaScript specs in this Rails project. The best thing – it just works (the Rails way).

    # add teaspoon dependency
    $ cd test/dummy/
    $ rails generate teaspoon:install
    # write a few specs
    $ rails s -p 8889
    

    You start a server, visit the url in the browser and the specs are executed

    Tests are pure JavaScript and we use Jasmine.

    That’s it. Not much to add. It’s simple The specs are a simple JS file located in “spec/javascripts/” folder and here is an example for one of them

    describe("IS.EventDef", function() {
      describe("constructing throws error if", function() {
        it("event name is null", function() {
          expect(() => new IS.EventDef(null, {})).toThrow(new Error("Null argument passed."));
        });
    
        it("event conf is null", function() {
          expect(() => new IS.EventDef("smoe", null)).toThrow(new Error("Null argument passed."));
        });
    
        it("declaring an event, but the class attribute value is null", function() {
          expect(() => {
            const ext = IS.ExtensionDef.CreateByExtensionConf({
              extension: new IS.Extension(),
              events: {
                declaredEvent: {
                  class: null
                }
              }
            });
          }).toThrow("Declared event 'declaredEvent' class attribute is null!");
        });
      });
    });
    

     
  • kmitov 10:10 am on December 10, 2020 Permalink |
    Tags: javascript, ,   

    Send array params to a rails server from a JavaScript code – URL and URLSearchParams 

    The goal was to filter building instructions on buildin3d.com by brand. We had to parse and create the URL on the client so I played around with URL and URLSearchParams (again). I will try to summarize the implementation here in the hope that you could help understand how URL and URLSearchParams work and how to use them with a Ruby on Rails app.

    Rails Server side request with array param

    The server accepts a request in the form:

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2&in_categories[]=13

    This means we could pass an array with the ids of the categories. The result will return only the 3D assembly instructions that are for Brands in these categories.

    How to send the request on the client side

    On the client side we have an <ul> element with some <li> elements representing the categories.

    The brands filter is on the left.

    When we click on the brand we would like to add the brand to the URL and redirect the user to the new URL.

    So if the current url is

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2

    and we select a new brand I would like to send the user to

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2&in_categories[]=13

    How to add array params to the URL with JavaScript

    Here is the whole code of the Stimulus JS controller

    import { Controller } from "stimulus";
    
    /**
     * This is a controller used for filtering by brands [categories] on the materials index page
     *
     * @author Kiril Mitov
     */
    export default class extends Controller {
      static targets = ["tree"];
    
      connect() {
        console.log("connect");
        const scope = this;
        this.setFromUrl();
        this.treeTarget.addEventListener("click", e => {
          e.preventDefault();
          const li = e.target.closest("li");
          const input = li.querySelector("input");
          input.checked = !input.checked;
          scope.goToNewLocation();
        });
      }
    
      setFromUrl() {
        const url = new URL(window.location);
        const categoryIds = new URL(window.location).searchParams.getAll("in_categories[]")
    
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .forEach(li => {
            const input = li.querySelector("input");
            const selected = categoryIds.indexOf(li.dataset["categoryId"]) != -1
            input.checked = selected;
          });
      }
    
      goToNewLocation() {
        const url = new URL(window.location)
        const searchParams = url.searchParams;
        searchParams.delete("in_categories[]");
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .filter(li => li.querySelector("input").checked)
          .forEach(li => searchParams.append("in_categories[]",li.dataset["categoryId"]))
        searchParams.sort();
        window.location = url.toString();
      }
    }
    

    There are a few important things in the code

    Opening the page on a new location

    window.location = url.toString()

    This will open the new page for the user

    Adding the selected brands to the url search query

    We listen for an event of click from the user and we get a list of all the checked brands.

    goToNewLocation() {
        const url = new URL(window.location)
        const searchParams = url.searchParams;
        searchParams.delete("in_categories[]");
        Array.from(this.treeTarget.querySelectorAll("li[data-category-id]"))
          .filter(li => li.querySelector("input").checked)
          .forEach(li => searchParams.append("in_categories[]",li.dataset["categoryId"]))
        searchParams.sort();
        window.location = url.toString();
      }

    First we delete the param “in_categories[]”. We use URLSearchParams.delete. This removes the param from the searchParam and if we then call .toString() we would receive the new search query without this param.

    Then we call this.treeTarget.querySelectorAll to filter all the checkboxes and then only the check once and we append “in_categories[]” param for every selected checkbox. This is what the server requires.

    searchParams.append("in_categories[]",li.dataset["categoryId"])
    

    As a result with have the query

    https://platform.buildin3d.com/instructions?in_categories[]=1&in_categories[]=2

    Because we use only URLSearchParam.append and URLSearchParam.delete all the other params are still in the search query.

    As a summary:

    We have a Ruby on Rails server that accepts an array param and we form this array param in a Stimulus JS controller. We use URLSearchParams method to append and delete params, as this will preserve the other params that are already in the url.

     
  • kmitov 9:56 pm on November 28, 2020 Permalink |
    Tags: , , javascript   

    How to do headless specs with the BABYLON JS NullEngine 

    [Everyday code]

    In buildin3d.com we are using BABYLON JS. To develop a headless specs for BABYLON JS that could run in a Node.js environment or without the need of an actual canvas we can use BABYLON.NullEngine. A spec could then look like

     const engine = new BABYLON.NullEngine();
     this.scene = new BABYLON.Scene(engine);

    Here is what I found out.

    Headless specs

    We run a lot of specs for our BABYLON JS logic. All of this specs are against preview.babylonjs.com. The preview version of babylon give us access to the latest most recent changes of babylon that are mode available to the public. These are still not release changes, but are the work of progress of the framework. They are quite stable so I guess at least the internal suite of BABYLON has passed. Preview is much like a nightly build. In BABYLON JS case it is also quite stable.

    2 days ago much of our specs failed. I reported at https://forum.babylonjs.com/t/failure-error-typeerror-cannot-read-property-trackubosinframe-of-undefined/16087. There were a lot of errors for :

    Cannot read property 'trackUbosInFrame' of undefined.

    Turns out that many of our specs were using BABYLON.Engine to construct the scene like

    const engine = new BABYLON.Engine();
    this.scene = new BABYLON.Scene(engine);

    The BABYLON.Engine class is only intended for the cases where we have a canvas. What we should have been doing for headless specs without a canvas is to use BABYLON.NullEngine. Hope you find it helpful.

    Write more specs.

     
  • kmitov 11:05 am on November 16, 2020 Permalink |
    Tags: , animations, , javascript   

    We got featured in the BABYLON JS 4.2 release video 

    About two year ago when we started working on delivering 3D instructions and playing 3D animations and visualizing 3D models in the browser. Little did we know than that with version 4.2 babylon js will feature us in their release video. Thank you BABYLON theme.

    I wrote an article on BuildIn3D.com – https://buildin3d.com/blog/buildin3d-featured-in-the-babylon-js-4-2-release-video/

    Here is the release video for all to enjoy.

     
  • kmitov 6:48 am on November 2, 2020 Permalink |
    Tags: , javascript,   

    A week ago I gave a nice lecture about Google Closure Compiler and how to use it in ADVANCED_OPTIMIZATION mode. It is available in Bulgarian at https://softuni.bg/trainings/3194/advance-javascript-compilation-with-google-closure-compiler-why-and-how

     
  • kmitov 5:54 am on October 21, 2020 Permalink |
    Tags: javascript   

    How I became one of the top JavaScript developers I know of? 

    TL; DR; How do you measure how good you are? – my highly unscientific measurement is around “when others start asking and listening to you” and when you “start delivering faster and better results”. I became really good in three steps – start solving a really hard problem, ditch the JavaScript dependencies coming from the community and pack everything.

    Context

    If you’ve been in the field of Software development and you are already a good developer and you’ve used but are generally avoiding JavaScript (because reasons), then this article is just for you. It sounds oddly specific, but in the same time I am constantly seeing many people in this situation. This was me a few years ago so I hope it could help you.

    In about 1.5 years I’ve become one of the best JavaScript developers I know of. The moment I personally started delivering better results than the much more experienced contractors I was hiring, while I was also managing our organization, I knew I have moved a long path.

    What was wrong with JavaScript?

    Nothing in particular and everything. We’ve seen countless articles from well read and published software developers over the last two decades about why JavaScript was “bad”. I am not going to get philosophical here, because I found out that they are all true and yet what stands behind the notion of JavaScript in the beginning of the third decade of the 21 century is still a very vibrant and live community achieving incredible results.

    Step one. Find a difficult real-world problem.

    My problem was to build an event-driven plug-in architecture in JavaScript while incorporating and refactoring legacy code that became one of the extensions. This was version 6 of our Instructions Steps Framework delivering the 3D assembly instructions at buildin3d.com. We are currently working about a hundred extensions.

    Finding a difficult problem could really focus you. It was not difficult from an algorithmic or mathematical point of view. It was not difficult as a new blockchain or AI or another emerging technology. It was difficult from an architectural point of view. How do you build a framework for plugins that live in a browser and that must incorporate some legacy behavior.

    I guess finding a difficult problem applies to learning all kinds of technologies.

    Step two. Ditch the dependencies. Go vanilla.

    The community is large. We have JS in the browser, in native apps, on the server. We have JS everywhere. The entry barrier is extremely low. You can build a new JS tool today, publish it on Reddit and have hundreds of people trying it tomorrow. In half a year your have hundreds of npm packages that are dependent on you, you change something and the whole npm is broken. This happens more often than you might think.

    Most of the solutions that could be found on the internet are outdated or plain wrong. Most of the solutions are not about the problem that is addressed. I guess because of the size of the community there are so many “answers and solutions” on the internet around – “take this library and do this call”, but these are not solutions for the case you are working on and are solutions that surely will not work in a year or two.

    Example. I’ve never in my live done something like:

    $ mv --version 1.2 source.md target.md

    This is a command for moving the file source.md to target.md where I’ve specified the “version” of the mv that is to be used. Does this sound familiar? No, of course. I am not sure even if it is possible to specify the version of mv to be used. I had an enormous difficulty explaining this to the maintainers of some of the popular tools used in the JS world and they did not agree with me. (No link here because I don’t want to point fingers.)

    I can not forget an interesting lecture by Linus Torvalds that I once watched where he was:

    You never break libraries ABI (Application Binary Interface). This is the thing I am very serious about in Linux Kernel. You don’t break ABI. You support it.

    But that is not how much of the JS community and in fact many other communities think.

    So I ditched the dependencies. I went full scale vanilla JS. I was not even depending on the DOM for much of the implementation and the things that were dependent on the DOM and some of the WEB APIs were extracted into proper extensions. Without any dependencies I had to do more work, and I would admit I had to write a tree traverse and observers on my own which I never expected I would have to. But at the end this ends up to be trivial implementations extracted to a proper extension that we could easily change with a new more well maintained library (when I find one).

    Step three. Pack and Go.

    Webpack it is. There are others. I’ve used many tools and I found out it is extremely important to pack your JavaScript properly and to understand the whole process. So look at the tool for packaging and try to develop the JS into different modules but pack them all into a single file that you deliver to the browser. The amount of issues you could face with this is really astonishing.

    Step four. Debug a jQuery problem.

    It took me weeks to migrate one of the platforms that we are running from jQuery 1 to jQuery 3.5. The amount of work required to completely remove jQuery dependencies was still too much so we decided to first migrate it and probably later remove it. Our code was not dependent on jQuery1, but some of our dependencies were. I have a friend that is now migrating from jQuery 3.4 to jQuery 3.5. I call these things the “jQuery problem” – as the library made it so easy to approach and solve different problems it was used in all kinds of unintended ways that it was never designed to. As the answers in blog posts and internet forms were based mostly around jQuery there was a decade or two with a lot of copy/pasting going on. Debugging such hard problems could be an eye opener.

    Conclusion

    A lot of other things matter – like choosing the right tools. But in retrospect these were the things I learned most from.

     
  • kmitov 4:22 am on October 21, 2020 Permalink |
    Tags: , javascript   

    Advanced compilation with Google Closure Compiler 

    Tonight I am doing a live stream lecture about Google Closure Compiler and how we use it to compile the JavaScript code of the Instructions Steps Framework. The framework is the core of buildin3d.com where we deliver 3D assembly instructions.

    The repo for the lecture is located at https://github.com/thebravoman/google-closure-compiler-presentation/.

     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel