Improving Your Development Documentation Project

PART 1

What this article covers
First steps on how to improve an existing documentation project.

Tools
Confluence, GitHub web and desktop versions, and MarkdownPad2.


Introduction

Developer Documentation is a curated set of files describing all the active workflowssetupstoolsconventionsbest practices, and How-tos of your software development product. Through this article, I will refer to it as “documentation” or “docs”.

Documentation supports your team members in their daily and future developments. It also helps new joiners to reach cruise speed during the onboarding period. But to do so, your documentation must be up-to-date and well-structured.

Keeping the docs up-to-date and in good shape requires resources and dedicated time. Yet often our project time or budget constraints prevent us from taking care of our docs properly.

This series of articles aims to serve as a documentation improvement guide.

Know Your Ground

Step 1 – Organize Your Improvement Project

Developer documentation has to be visible to increase the chances of success, and to find collaborators (to improve it). To do so, keeping a space to visualize, describe and track your improvement project is a useful idea.

Use your teams/company collaboration tool for that purpose. For this article, we’ll be using Confluence.

Space Structure

The structure of an improvement project may differ from one project to another. Take the following space structure as a reference that you can adapt to your needs (then iterate!):

Space NamePage NameChild PageDescription
[Your Documentation Project´s Name]Name of your documentation project.
OverviewExplain briefly the What, Why and How of the documentation.
DashboardCentralized page to easily access all project pages.
AnalysisMedia and results of documentation analysis.
RoadmapVisualization of the estimated dates to implement each improvement.
Improvement ProjectCommunication GridContact person by topic.
Improvement PlanImplementation phases and items.
Coordination MeetingsGrid to align with your manager or collaborators (Optional).

Once your improvement project space is set up, you are ready to:

• Present it to all your team members, including Product Owners and Scrum Masters.
• Track and show your progress.
• Visualize documentation issues/blocking points.
• Access all your project resources.

Tip: Explain how documentation issues negatively impact teams´ performance. It will help Product Owners and Scrum Masters to understand and provide your project with the resources you need.

Step 2 – Identify Your Documentation Issues

Identifying your documentation issues means spotting all the types of issues living among your docs. Some documentation issues are:

• Grammar, spelling and syntax errors.
• Confusing/Not logical page structure.
• Unclear/Verbose text.

In the following table, you can learn a little more about the main documentation issues:

Documentation IssueFix strategy
Grammar, spelling, syntaxUse a text checker to support your writing. Some good options are GrammarlyHemingway or QuillBot.
Page structureReview your page structure. It shows the logical flow of the information contained according to the objective of the page, for example: IntroductionPrerequisitesFirst StepWorking with…/Available Features.
NamingDefine a naming strategy for page titles, sections and subsections.
Page elementsStandardize the use of the following elements: lists, tables, tabs, notes, collapsible elements and images.
Text unclear or too verboseBe concise.
Random text formattingStandardize the use of bold and italics for files, folder names, code snippets and code elements (functions, objects, methods, parameters, API names, etc.).
Too many topics on a single pageStick to “One topic per page”.
Unnecessary screenshotsUse screenshots or images ONLY when strictly necessary. If you can explain it briefly, do not use screenshots.
Type of notesStandardize each type of use case for notes (Info, Help, Warning, etc.).

Now we can start to target and record the issues of our documentation. The following table will help you to perform that task:

Nav OptionPageSectionSubsectionIssueLink
Add nav. option namePage numberSection nameSubsection nameIssue name

Depending on the size and complexity of your documentation, targeting these basic issues may take a while. Take the chance and join me on the journey to better documentation and improve your documentation project now.


To Be Continued…

What’s Next?
In the next article, we will describe how to run a user survey to gather useful feedback from your users/readers. This invaluable feedback will help us prioritize the documentation issues to fix first.

Productivity series: Spotlight replacement

Introduction

Continuing with the theme of my previous article (sharing my MacOS menubar setup), I’d like to show you how I used Spotlight and why I replaced it with another tool. The app is called Raycast and it’s a real productivity swiss army knife.

It does everything Spotlight does plus some built-in features like Clipboard History, Snippets with text expansion, bookmarks search, calendar agenda, timers, reminders, convert units, math, etc. But it also has an Extension Store, where you can download community-contributed extensions/integrations. At the end of the article, I will share a few that I’m using.

If you are new to macOS or don’t use Spotlight, let’s see why the omnipresent search bar is powerful.


Why Spotlight is great

Spotlight is a system-wide desktop search feature of Apple’s macOS and iOS operating systems

You can open it via CMD + Space shortcut keys.

I used Spotlight mostly for opening and switching between apps. There are many ways to do just that, but I don’t know any faster way than hitting Cmd + Space and typing in the first letter of the app and hitting Enter.

My macOS dock is always set to hide, not to take that precious screen real estate on small laptop screens. And when you train (by using) Spotlight what each search letter opens, I don’t see why anybody would use Dock, or Mission Control to switch apps.


Moving away from Spotlight to Alfred

I felt one could do even more from this powerful input box so I stumbled into Alfred.

Alfred comes with a Clipboard manager, bookmark search, custom web search, etc. However, those are premium features, and it costs ~40€ to get those.

Next to that, as powerful as it is, Alfred looks like outdated software.


Here comes Raycast

After some searching, I found out about Raycast, and for a shortcut person like myself, it blew me away 🤯.

Out of the box, it does everything mentioned tools do and then some. But unlike them, it has a built in Extension Store, where you can install various community-contributed extensions.

You get all that completely free for personal use. See more about pricing plans on their website.


Features I use the most

I will go over some of the built-in features I use the most and will end it with a couple of extensions I installed myself.

Open or switch to any app quickly

When you type in the Raycast input box, it will remember which result you used with that query. This is how you train Raycast to be more relevant to you.

For example, I use one-letter shortcuts to open many apps. To open Outlook, I press o, to open Chrome, I press c, to open Visual Studio Code, I press vs… When your result is highlighted, press Enter and the app will open/focus.

Clipboard history

Remember how many times you copy something, and you need to paste it again, but now your clipboard contains different content, so you can’t paste.

Clipboard History saves what you have copied and if you need to paste it again, you press a shortcut key to open Clipboard history and search what you want to paste or use your keyboard/mouse to navigate to an item and paste it directly.

It's a screenshot from the clipboard history of the author of this article on Raycast.

In this extension settings, you can configure a custom keyboard shortcut to show the Clipboard History window at any time. I use CMD + Shift + V. Notice how the usual paste with CMD + V still works like before.

To configure a global hotkey for an extension, open Raycast and then CMD + ,to navigate to Raycast Preferences. Follow the steps from the screenshot to get an overview of extension settings and change them to your liking.

As a software developer, I use Jira a lot. With that comes a lot of copying and pasting of Jira issue numbers. Some nice people share links to tickets, and some others, just use ticket numbers as plain text. To be able to navigate to that ticket in Jira quickly, you can use Quick Links.

You do this by defining a custom URL and the dynamic part of it. So when you paste something in that dynamic part, it will open that link in your browser. Optionally, you can give an alias to your Quick Link for easy and quick access.

In the end, this looks like this:

• Copy issue number
• Press CMD + Space to open Raycast
• Press j (for Jira)
• Press space to focus on the dynamic part of the URL
• Paste the issue number
• Press Return key to open it in a new browser tab

Make sure to check out extension settings and adjust them to your needs.

Opening bookmarks

On any given day, I open vast amounts of the same websites, eg. Jenkins jobs for builds and deployments, production Jenkins jobs, specific pages in Confluence, GitHub, team calendar in Confluence, and the list goes on and on.

Sure, one could use the browser bookmarks bar, but that requires using the mouse and clicking more than a few times just to open one page.

When you use the Search browser bookmarks extension (to which you assign a nice alias eg. b), then all you need to do is:

• Open Raycast prompt (CMD + space)
• Press b (alias for browser bookmarks extension)
• Press space and start typing
• Press Return key when your result is highlighted

Notice how you can bookmark any different page in Jenkins, so you have very quick access to those pages, without the need to navigate using the mouse or manipulating URLs. Once you give meaningful names to your bookmarks, opening them is super fast.

Snippets

If you find yourself needing to type something repeatedly, eg. some code snippet, URL, greeting, full name, date, etc. this one is a true time-saver. I started using a global text replacement tool called Espanso some time ago, but I didn’t find it working reliably. What was cool about it, it would replace predefined shortcut text eg. :br with Best Regards as you type.

But since Raycast can do the same, I just uninstalled it and configured my shortcuts using Raycast snippets.

For example, to open our INT environment, I type in the URL bar :int and this expands to a snippet I have defied in the snippets collection.

I am pretty sure you type in your email at least once a day. If you have a long name, you can configure a snippet for your email. By giving it a shortcut it is easy as:

• Focus on any input field or text editor
• Type :mail
• Raycast will automatically expand that to your email.

Snippets are even more powerful than that. You can even configure parts of the snippet to be dynamic, eg. dates and time, and even where to place the cursor.

One snippet I use a lot is to access a deep object in the Redux store. For that, I have shortcuts eg: :pal and this expands to someStore.store.store.getState()['SOME_OBJECT'].

Notice how much typing that saves.

Rest of the extensions

There are many more extensions to be discovered and used in the Raycast extension store.

With Raycast prompt open, type Store and install the ones you find useful. Or just navigate to the website and browse over there.

I can recommend: Reminders, Timers, Window management, One ThingDepcast, Brew, Kill process, etc… or just doing a prompt like: 1m to in, or 34usd to eur.

In conclusion

I can not possibly cover all the features of Raycast in one blog post. However, I hope I have shown you a new tool to be added to your arsenal should you like it.

For me, it makes mundane daily tasks a bit more fun and quicker to do. And since Raycast can do so much, it made me uninstall many other apps and simplify my setup.

I hope you had fun reading. Now go explore Raycast and let me know which are your favorite features.

Don’t forget you can type raycast in Raycast prompt. There is a handy Walkthrough feature 😉

You Might Not Need Module Federation: Orchestrate your Microfrontends at Runtime with Import Maps

TL;DR

Managing microfrontends in a complex feature-rich app can become a tedious task and easily turn it into a Frankenstein’s monster when there’s no clear strategy involved.

Using third-party tools like Webpack Module Federation helps to streamline the building and loading of microfrontends, but leads to vendor lock-in, which can be a problem.

Import Maps can be seen as a web native alternative to Webpack Module Federation to manage microfrontends at runtime. In this article, we will:

• Explore the concept of Import Maps

• Build a demo app

• Summarize the pros & cons

Import Maps in a nutshell

The concept of Import Maps was born in 2018 and made its long way until it was declared a new web standard implemented by Chrome in 2021 and some other browsers.

Import Maps let you define a JSON where the keys are ES module names and the values are versioned/digested file paths, for example:

<script type="importmap">
  {
    "imports": {
      "my-component": "https://assets.mycompany.io/scripts/my-component/index.5475c608.js"
    }
  }
</script>

Such mapping can be resolved directly in the browser, so you can build apps with ES modules without the need for transpiling or bundling. This frees you from needing Vite, Webpack, npm, or similar.

Import maps allow us to write:

import 'my-component';

instead of the following:

import 'https://assets.mycompany.io/scripts/my-component/index.5475c608.js';

and let the browser resolve the actual path at runtime.

Advanced Import Maps features

You can reuse an import specifier (for example lodash below) to refer to different versions of the same library by using scopes. This allows you to change the meaning of an import within that given scope. As a result, any module in the https://assets.mycompany.io/scripts/my-components path will be using lodash-es version 3.9.3 while all other modules will use version 4.17.21.

<script type="importmap">
  {
    "imports": {
      "my-component": "https://assets.mycompany.io/scripts/my-component/index.5475c608.js",
      // allows import { my-component } from "component-library" syntax
      "component-library/": "https://assets.mycompany.io/scripts/my-components/",
      "lodash": "https://esm.sh/lodash-es@4.17.21", ⬅
      "lazyload": "IntersectionObserver" in window ? "./lazyload.js" : "./lazyload-fallback.js",
    },
    "scopes": {
      "https://assets.mycompany.io/scripts/my-components/": {
        "lodash": "https://esm.sh/lodash-es@3.9.3" ⬅
      }
    }
  }
</script>

You can also construct an import map dynamically based on conditions: the example above taken from this article shows how you can load different modules based on the support of IntersectionObserver API.


Demo app

In this article, we bring the idea of Import Maps further by placing an Import Map between the host app and microfrontends and applying the dependency inversion principle. This makes the host app not directly dependent on a concrete microfrontend version or its location, but rather on its abstraction via name or alias.

We are going to build an online store that has only one, but highly customizable assortment type: T-shirt.

Step 1: Outline the Architecture

There is an arbitrary number of microfrontends that are assigned to the development teams, each of them is free to choose the tech stack, build and CI/CD tools. The “only” constraint is to make sure each build pipeline produces 3 artefacts: ESM bundleManifest and other Static Assets.

The lightweight Nest.js Import Map Resolver server has two main roles: store and update the importmap, but also handle the submission of JS assets. Single-spa has a similar solution available.

The Publisher will read your Manifest, extract the bundle filename as well as externalized dependencies and publish them to the Import Map Resolver.

The Assets Server is used as a Web-enabled storage to host JS assets. To store images, videos, and other assets we can choose an arbitrary Storage, for example, an Amazon S3 bucket. CDN is used to serve third-party libs and frameworks as ES modules, a good one is ESM.sh.

ESM bundle

Your production-ready application ESM bundle is generated by Webpack, Vite, Rollup or any other bundler of your choice. For simplicity of the setup, CSS Injected By JS plugin for Vite is used along with the scoped styles to build a single ES module with injected CSS.

If your build produces more than one bundle (for example, due to code splitting), you have two options:

  • concatenate them after the build, for example via concat
  • alter the Publisher to loop over the multiple entry chunks and add the prefix, e.g.: my-component:mainmy-component:polyfill, and so on.

Manifest

This is a JSON file that contains the mapping of non-hashed asset filenames to their hashed versions and, if you are using Vite, you just need to add manifest: true to your Vite config. This will produce the following file in the /dist of your project:

{
  "main.js": {
    "file": "assets/index.fb458d74.js",
    "isEntry": true
  },
  "views/cart.js": {
    "file": "assets/foo.869aea0d.js"
  }
}

The generated Manifest will be used by the Publisher to know the mapping of your microfrontend unique name to its ESM bundle.

Static assets

Everything else, such as images, videos and other static files required by your microfrontend.

Step 2: Define the UI and split into microfrontends

Our online store demo app will have three views: Home, Product & Cart:

Vue is used as a core “metaframework” to have out-of-the-box routing, simple state management with Pinia and Vite as a bundler. It is not necessary at all to use the “metaframework”, moreover, during the build, you’ll get errors from Vite’s internal import-analysis plugin because of unresolved imports (good news, there is a solution for that, see “Challenges → Metaframework”).

To demonstrate how several microfrontends can co-exist together on the same page, they are built with four different frameworks. To make each app’s setup look similar, Vite template presets are used to generate Vue, React, Lit and Svelte microfrontends that are compiled into Web Components. You may consider splitting your app by functional area and building your microfrontends around business domains, such as Order, Product, etc.

Step 3: Build the app

The full source of the Demo app can be found here.


Common problems & solutions

Take control away from the bundler when resolving imports

How do bundlers work? If you ignore the implementation details and go to the highest level of abstraction, they concatenate all the jungle of JS modules and put them into one big chunk, that is minified, uglified, and tree-shaked to get rid of unused code. Simple concatenation wouldn’t work. You need to indicate the entry point and make sure you don’t have modules that import themselves – cyclic dependencies. Most of the bundlers solve this by building an abstract syntax tree. For example, Rollup does it with Acorn.

Using micro-frontends resolved via Import Maps introduces a challenge for your bundler that should normally know your dependencies at compile time, not at runtime. We need to tell Rollup to stop building the tree once a dependency from the Import Map is met and make the current module a leaf node.

Luckily, Vite, Rollup and Webpack have options to take control away from the bundler and let the browser resolve the specified imports by providing their names in the configuration.

import { defineConfig } from "vite";

export default defineConfig({
  build: {
    rollupOptions: {
      external: [
        "widget",
        "some-other-widget",
        "lodash"
      ],
    },
  },
});

Load import map dynamically

Specs say that “any import maps must be present and successfully fetched before any module resolution is done”. Essentially, it means that the importmap must be inserted in the DOM earlier than any other async script.

Vite is internally using build-html plugin, that produces index.html with the entry point added via <script type="module" src="bundle.js> tag to the <head> section. This is not what we want. Instead, we would like to execute a script that will fetch the import map first, add it to the page, and then load the app script.

To build a custom index.html the Async Import Map plugin for Vite was created that is internally using Rollup Plugin HTML. The plugin extracts the entry point script from the list of generated assets (by lookup for isEntry: true), stashes it, loads the import map from the specified URL and then unstashes and appends the entry point script, giving control back to your app.

See full source here.

Shared state and communication

Eventually, there are only 6 ways to share the state across microfrontends:

  • Windowed Observable uses the global window object as the medium to share the data, often wrapped into a pub-sub library
  • Web storage, such as Local Storage, Session Storage or Cookies
  • URL via query / params
  • In-memory (e.g. Redux)
  • Backend (session or persisted state)
  • Props and Custom events / Callbacks

Everything else you may come across could be just an abstraction on top of these methods. Here is a good summary of their pros and cons.

Since the goal is to use as many native web capabilities and avoid vendor lock-in, we can stick to Props & Custom Events. One important note to mention: to let an event “escape” from the Shadow DOM, we need to set bubbles: true and composed: true. This way we make sure events propagate through the parent-child as well as the shadow tree hierarchy. A nice explanation can be found here.

document.dispatchEvent(
  new CustomEvent("select-color", {
    bubbles: true,
    composed: true,
    detail: 'blue',
  })
);

Shared dependencies

To share your microfrontend dependencies, you can declare them as “external” by providing them in the configuration as follows:

import { defineConfig } from "vite";

export default defineConfig({
  build: {
    rollupOptions: {
      external: ["react", "react-dom", "react-to-webcomponent"],
      output: {
        globals: {
          react: "react",
          reactDom: "react-dom",
          reactToWebComponent: "react-to-webcomponent",
        },
      },
    },
  },
});

Here we are telling Rollup to not bundle React dependencies as well as to provide global variables for them.

But how do we deal with dependency mismatches when one or more microfrontends are using the same lib, but with different versions? Let’s say Footer and Header are two React major versions apart. As mentioned before, we can use scopes:

<script type="importmap">
  {
    "imports": {
      "header": "/path/to/header/index.5475c608.js",
      "footer": "/path/to/footer/index.6087f008.js",
      "react": "https://esm.sh/react"
    },
    "scopes": {
      "/path/to/header/": {
        "react": "https://esm.sh/react@16.13.1"
      },
      "/path/to/footer/": {
        "react": "https://esm.sh/react@18.2.0"
      }
    }
  }
</script>

Alternatively, we can provide different import specifiers:

<script type="importmap">
  {
    "imports": {
      "header": "/path/to/header/index.5475c608.js",
      "footer": "/path/to/footer/index.6087f008.js",
      "react@16": "https://esm.sh/react@16.13.1",
      "react@18": "https://esm.sh/react@18.2.0",
    }
  }
</script>

If you need some sophisticated logic to build your import map, Import Map Resolver is the place to put it. Let’s say one of your microfrontends publishes its new version that uses react@17.0.1, but you already have react@17.0.0 in your importmap. In this case, the Import Map Resolver would remove an older version and replace it with the newest one. It is one minor version ahead, assuming backward compatibility is guaranteed.

Library microfrontend

Microfrontends can be published as a Custom Components library.

Example using Vite and Svelte:

import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  build: {
    rollupOptions: {
      input: [
        './src/Header.svelte',
        './src/Footer.svelte'
      ],
    }
  },
  plugins: [
    svelte({
      compilerOptions: {
        customElement: true,
      },
    })
  ]
});

This will produce two separate chunks, one for the Header and one for the Footer. Vite supports library Mode for Vue and other frameworks.

Without going into each library configuration details, the general principle is to alter your main.ts entry point (or each of your entry points if they are many) in a way you’d like to expose your microfrontend defined as a Custom Element.

import MyComponent from '.src/my-component';

customElements.define("my-component", componentWrapperFn(MyComponent));

where componentWrapperFn is a function provided by your (or a third-party) library that returns a custom element constructor that extends HTMLElement. It could be native defineCustomElement in the case of Vue or third-party reactToWebComponent from react-to-webcomponentHere (and also here) is a great summary of how to build Web Components with different libraries and frameworks.

Metaframework

As mentioned in the section Demo app, a metaframework is used to glue the microfrontends together. Choosing no framework is also a valid option. Import Maps perfectly support this case by resolving imports directly in the browser. The choice of using Vue is mainly to avoid writing boilerplate code for routing and make the container components lean, having little to no low-level DOM manipulation. There is a good article explaining why we need the container components and how to structure them.

Routing

Routing between container components/pages is covered by the metaframework in case you are using one. If not, you can opt for Navigo as a simple dependency-free JS router.

In rare cases, when you need navigation within individual microfrontends this is where it gets tricky: at the end, you only have one address bar. You can map the flattened structure of your compound URL state (for example, map https://my-app.com/mfe-1:article/search and https://my-app.com/mfe-3:invoice/edit/8 to https://my-app.com/(mfe-1:article/search//mfe-2:invoice/edit/8)) to enable two-level routing with the help of your framework. There is a library for Angular that uses URL Serializer to intercept and manipulate browser URLs.

That being said, this approach also introduces an unwanted tight coupling among microfrontends: the host app shouldn’t know the details of individual microfrontends routing. When a team needs to change or add a new URL, the host app would need to be adjusted and redeployed. Instead, try avoiding two-level routing at the stage of application design. To better understand all the consequences of this approach, you may want to read the book Micro Frontends in Action by Michael Geers, chapter “App shell with two-level routing”.


Pros

Let’s summarize all the benefits that Import Maps offer:

• flexibility for microfrontend teams (each team can have its own tech stack, CI/CD, coding guidelines, infrastructure: everything before final artefacts are built)

• easy step-by-step migration of existing codebases by replacing components or entire pages with microfrontends

• the host app is lean and detached from the development of microfrontends and focuses on the composition of pages, providing the data and handling events

• the host app is not aware neither of the existence nor the implementation details of your microfrontends: the only “contract” is the API of your microfrontend (props/events)

• import map entries are lazy-loaded: no JS is downloaded before you actually import()

• you may not need any build tools at all: import maps work in the browser at runtime

• it takes seconds to update your app (by changing entry in the import map)

• it takes seconds to rollback

Cons

Let’s summarize all the drawbacks that the usage of Import Maps brings:

• import Maps are not supported in some browsers, however, there are polyfills

• the overall amount of bytes downloaded when using microfrontends in comparison to monolith is unavoidably higher. Even though you can externalize and share your app dependencies, you cannot avoid eventual code duplication in individual microfrontends

• not suitable for small and medium size projects where single core framework would be a better fit

Conclusion

Using Module Federation in comparison to Import Maps has a major drawback, which is vendor lock-in, that makes your product dependent on another product: Webpack. All your micro-frontends as well as your host app must comply with it and be on the correct version. You also cannot avoid the compilation step, while Import Maps can be used directly in the browser.

At the same time, new web standards are emerging and replacing the need for third-party products. While the standard is being developed further, getting new features, such as multiple import map support and programmatic API, you can already start using them now with the help of ES Module Shims or System JS.

Photo by AltumCode on Unsplash

Our top 5 topics in November by the tech practice circle

This month, the developers had stimulating conversations on MS Teams, sharing their opinions.

Sticky Scroll

Many of our frontend developers at MB.io use Visual Studio Code. The VS Code team has released a new setting that helps you keep track of your file. Sticky Scroll, this feature shows the class/function you are currently working on at the top of the editor. Just enable it in the settings: “editor.stickyScroll.enabled”. Watch the short video and let it explain the behavior.

The Microsoft Edge Dev Tools extension for VS Code

The title of this article was so catchy that it spoke directly to me: “The Microsoft Edge Dev Tools extension for VS Code is so awesome that I’m ditching Chrome for web development”. What do you think? Will you try the visual studio code extension?

State of frontend 2022

Over the past two years, the IT industry has been undergoing major changes, especially in the area of frontend development. In this report, 3703 frontend professionals from 125 countries and 19 frontend experts were surveyed to get an accurate overview of current trends and the future of frontend. The goal is to provide insights on topics such as technologies, practices, and working conditions. This survey is a good starting point to discuss the insights in the team, to read trends for ourselves and to find out what we want to focus on in the future.

100 Seconds of Code

Curiosity and the will to learn something new every day is in the genes of us as developers. Especially easy to consume are the contents of this playlist: 100 Seconds of Code. Watch one of the clips every day and you will have 133 days of fresh input. The basics and commands for tools, technology or frameworks are covered.

Practical Accessibility a online video course

A self-paced, get-right-down-to-it online video course for web designers and developers who want to start creating more accessible websites and applications today. The course is by Sara Soueidan an inclusive design engineer, author, speaker, and trainer. Because accessibility is important to us at Mercedes-Benz.io, one of our A11y gurus in our company will definitely be watching the course.

free-for.dev

Experimenting and trying out innovative new tools and services is vital in the life of a developer. This git links collection gives you the opportunity to browse what’s out there and especially which is free to use for developers. From major cloud providers, to source code repos, web hosting, analytics, to game development, you name it. What will you try next?


Thanks to all who share their knowledge in our company in this way and use our communities to exchange ideas. Stay curious!

JOIN OUR TRIBE OF DIGITAL ENTHUSIASTS

View job openings

Photo by Tim Stief on Unsplash