<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yoann Moinet</title>
    <description>The latest articles on DEV Community by Yoann Moinet (@yoannmoinet).</description>
    <link>https://dev.to/yoannmoinet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51386%2F05ffc384-4b71-44dc-86ff-0d5eddccc382.jpg</url>
      <title>DEV Community: Yoann Moinet</title>
      <link>https://dev.to/yoannmoinet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yoannmoinet"/>
    <language>en</language>
    <item>
      <title>🧑‍💻 How to remain relevant in this AI era?</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Thu, 12 Feb 2026 16:16:36 +0000</pubDate>
      <link>https://dev.to/datadog-frontend-dev/how-to-remain-relevant-in-this-ai-era-1mle</link>
      <guid>https://dev.to/datadog-frontend-dev/how-to-remain-relevant-in-this-ai-era-1mle</guid>
      <description>&lt;p&gt;With the advent of AI, the software engineering job is evolving, but our responsibilities remain.&lt;/p&gt;

&lt;p&gt;Producing code is no longer the hardest or most valuable part of the job.&lt;br&gt;
Solving problems still very much is.&lt;/p&gt;

&lt;p&gt;That’s where the real shift is happening.&lt;/p&gt;

&lt;p&gt;AI won’t replace problem solvers.&lt;br&gt;
But it &lt;em&gt;will&lt;/em&gt; compress demand for pure code writers. It &lt;em&gt;will&lt;/em&gt; make pure code writing cheap, and eventually optional.&lt;/p&gt;

&lt;p&gt;Which side you’re on will largely determine your near future.&lt;br&gt;
So, how can you lean in the right direction, today?&lt;/p&gt;

&lt;h2&gt;
  
  
  Be an actor, not a spectator
&lt;/h2&gt;

&lt;p&gt;Watching from the sidelines will make you irrelevant faster than the actual impact of AI on the industry.&lt;br&gt;
Actors drive outcomes; spectators only consume outputs.&lt;/p&gt;

&lt;p&gt;This is where careers start to diverge.&lt;/p&gt;

&lt;p&gt;If you’re an actor, you’ll compound your value.&lt;br&gt;
Spectator? Anyone else can do it, even AI can do it.&lt;br&gt;
You’re interchangeable and eventually… replaceable.&lt;/p&gt;

&lt;p&gt;There are two main ways to be an actor today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tame and wield AI effectively&lt;/strong&gt;&lt;br&gt;
Learn to use it &lt;em&gt;well&lt;/em&gt;, in ways that are efficient for you.&lt;br&gt;
Best practices, workflows, skills, commands, prompt engineering, local setup, limitations …&lt;br&gt;
Treat it as a power tool, not a magic box.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expand your problem-solving surface area&lt;/strong&gt;&lt;br&gt;
Work on codebases you’re not familiar with.&lt;br&gt;
Use AI as a translation layer across languages, frameworks, and paradigms.&lt;br&gt;
Apply your problem-solving skills, your judgement, your taste and reasoning, to a much wider surface area.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Be intentional and explicit
&lt;/h2&gt;

&lt;p&gt;Never blindly accept AI output.&lt;br&gt;
Review it. Sanitise it. Improve it.&lt;br&gt;
Use your expertise, and yes, use AI again to refine it.&lt;/p&gt;

&lt;p&gt;Follow a clear, repeatable process, make it better, and stick to it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; 🤖 Plan | 👨‍💻👩‍💻 Chaperone&lt;/li&gt;
&lt;li&gt; 🤖 Execute | 👨‍💻👩‍💻 Chaperone&lt;/li&gt;
&lt;li&gt; 🤖 Verify | 👨‍💻👩‍💻 Chaperone&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any step can be done by AI.&lt;br&gt;
None should be fully delegated to AI.&lt;/p&gt;

&lt;p&gt;You remain accountable. You supervise. You decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your (new) job: tame the tool
&lt;/h2&gt;

&lt;p&gt;AI is just that. A tool.&lt;/p&gt;

&lt;p&gt;Your job is to know how to use it &lt;em&gt;effectively&lt;/em&gt;, &lt;em&gt;reliably&lt;/em&gt; and &lt;em&gt;safely&lt;/em&gt;.&lt;br&gt;
It is nothing more than an extension of your expertise. Not a replacement of it.&lt;/p&gt;

&lt;p&gt;People (used to) go to the circus to see lions.&lt;br&gt;
But, at the end of the day, the tamer is the one getting paid.&lt;/p&gt;

&lt;p&gt;In the past, staying relevant meant learning new languages, paradigms, frameworks, or platforms.&lt;br&gt;
Today, it also means learning how to work &lt;em&gt;with&lt;/em&gt; AI at a high level, because it only amplifies the expertise, taste, and judgement you already have.&lt;/p&gt;

&lt;p&gt;For engineers early in their career, this means doubling down on fundamentals.&lt;br&gt;
AI is only useful if you know what “good” looks like. It won’t give you opinions or instincts.&lt;br&gt;
Skipping fundamentals and jumping straight into AI is how you become a fast, confident producer of bad code.&lt;br&gt;
This is how you build experience: study opinionated work, internalise the trade-offs, and develop your own standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not delegate your responsibility
&lt;/h2&gt;

&lt;p&gt;Delegating responsibility is how you make yourself irrelevant, slowly but surely.&lt;/p&gt;

&lt;p&gt;Anyone can ask AI to generate code. My brother, a sushi chef, is doing it.&lt;br&gt;
It can look, and be, production ready. But without judgement, standards, and review, it will progressively turn into unmaintainable slop. (&lt;em&gt;Sorry bro'&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Very few can steer it precisely, validate its output, and make it &lt;em&gt;consistently&lt;/em&gt; useful.&lt;/p&gt;

&lt;p&gt;AI can produce good code, engineers produce systems.&lt;/p&gt;

&lt;p&gt;That's where your problem-solving skills matter most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  understand the problem&lt;/li&gt;
&lt;li&gt;  figure out the right solution&lt;/li&gt;
&lt;li&gt;  orchestrate its implementation&lt;/li&gt;
&lt;li&gt;  own the outcome&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good exercise to enforce this is to be able to precisely explain the problem, the solution and implementation.&lt;br&gt;
Unless you really understand them, you won’t be able to explain them to someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invest in the right things
&lt;/h2&gt;

&lt;p&gt;Experimenting with core models and first-party tooling is great.&lt;br&gt;
Obsessing over every new thin wrapper or orchestrator built on top of them is not.&lt;/p&gt;

&lt;p&gt;The pace of change is overwhelming. No one truly keeps up with everything, and that’s fine.&lt;br&gt;
What matters isn’t tracking every new tool, but working from first principles: pick a vanilla LLM, understand its core capabilities and limits, and experiment deliberately.&lt;/p&gt;

&lt;p&gt;Remain alert and open.&lt;/p&gt;

&lt;p&gt;Do not over-invest in niche wrappers, orchestrators, or other products that merely &lt;em&gt;patch&lt;/em&gt; temporary gaps in current agent capabilities.&lt;br&gt;
Many of those gaps will disappear within months, with a simple agent update.&lt;/p&gt;

&lt;p&gt;It’s the equivalent of mastering jQuery in the 2010s without understanding JavaScript.&lt;/p&gt;

&lt;p&gt;Retain the methodologies and the workflows.&lt;br&gt;
Not the particularities and specificities of the tool you’re using this week.&lt;/p&gt;

&lt;p&gt;Invest in learning how to use LLMs in general, how to steer them the best, what standards to apply.Not just which button to click.&lt;br&gt;
Think prompt engineering, skills, AGENTS.md, commands, things that &lt;em&gt;transfer across&lt;/em&gt; tools and generations.&lt;/p&gt;




&lt;p&gt;My optimistic hot take is, if anything, AI will ultimately &lt;em&gt;raise&lt;/em&gt; the overall level of code quality.&lt;br&gt;
It will widen the gap between bad code vs good code.&lt;/p&gt;

&lt;p&gt;Given how easy it becomes to build instead of buying, new products will need to be &lt;em&gt;exceptionally&lt;/em&gt; good to justify being acquired or paid for.&lt;/p&gt;

&lt;p&gt;It raises expectations. It raises the value of engineers who think, reason, and decide.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>🧑‍💻 Platform Teams best practices</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Wed, 15 Nov 2023 16:39:31 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/platform-teams-best-practices-30l1</link>
      <guid>https://dev.to/yoannmoinet/platform-teams-best-practices-30l1</guid>
      <description>&lt;h2&gt;
  
  
  Bon matin 👋
&lt;/h2&gt;

&lt;p&gt;I'm Yoann Moinet, a Frenchman living in Montpellier.&lt;br&gt;
In early 2019, I joined &lt;a href="https://datadoghq.com" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; and bootstrapped the &lt;strong&gt;Frontend Platform&lt;/strong&gt; team.&lt;/p&gt;

&lt;p&gt;But, what is a &lt;strong&gt;platform&lt;/strong&gt; team you ask?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[opinionated vision]&lt;/em&gt;&lt;br&gt;
They're here to improve the Developer eXperience and remove any pain points in the &lt;em&gt;day-to-day&lt;/em&gt; work of all the engineers.&lt;br&gt;
They can cover a lot of ground: build, tests, deployment, code health, internal tools, and even more...&lt;/p&gt;

&lt;p&gt;Let's say, in a company, the engineers are working on a product or a service to satisfy the customers. &lt;br&gt;
The platform team works on the platform to satisfy the engineers, building anything they can think of, to improve the reliability, productivity, efficiency and (more importantly) the happiness of our engineers shipping the product to our customers.&lt;br&gt;
&lt;em&gt;[/opinionated vision]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Working in a &lt;strong&gt;large scale&lt;/strong&gt; environment at Datadog, we had to come up with some kind of &lt;strong&gt;charter&lt;/strong&gt; so we stay focused on what's really important (DX) and don't try to fix everything at once: that would end up being a bad experience for everyone involved.&lt;/p&gt;

&lt;p&gt;You have to be laser focused on what you want to achieve and always work incrementally. &lt;br&gt;
There, I gave you the first best practice for free... &lt;br&gt;
&lt;em&gt;jk, they all are for free.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Over the years, I've identified a few best practices (most of them directly translated from our charter) that could be phrased agnostically enough to be applied not only to the frontend, but to any kind of platform team.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. 🏢 Workflows --not their implementation-- need to be shared company wide.
&lt;/h2&gt;

&lt;p&gt;You should share similar workflows between teams and technologies, so it's easy for newcomers or someone working on an incident to quickly get up to speed even in a different repository/project.&lt;/p&gt;

&lt;p&gt;Markers and primitives need to be identified in each workflow in order to keep them similar across implementations.&lt;/p&gt;

&lt;p&gt;These implementations &lt;strong&gt;can be different&lt;/strong&gt; as long as a team is there to own the support and follow the same identified markers/primitives.&lt;/p&gt;

&lt;p&gt;In the case where there is already an established tool foundation, you should have, at the very least, &lt;strong&gt;hooks/flexibility/documentation&lt;/strong&gt; to customize the workflow in order to align with the project's tech stack and complexity.&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;We have a command to deploy to staging from whichever repo you work on. &lt;br&gt;
This command waits for the feature branch's pipeline to be 100% ✅ before triggering a merge and deploy into our shared staging. &lt;/p&gt;

&lt;p&gt;For the frontend, in a dedicated repository, it's safe enough to deploy on staging at an earlier step in the pipeline, so we updated the global tool to only wait for a specific job in the CI before deploying. This way it can trigger much sooner for our frontend engineers.&lt;/p&gt;

&lt;p&gt;This alone reduced the staging workflow by half for our frontend teams without impacting the global workflow itself, and sharing the same primitives with the rest of the company.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  2. 💡 Workflows should not be created or changed unless it's tightly related to a known and documented problem.
&lt;/h2&gt;

&lt;p&gt;You may want to test new technologies, or read articles about new workflows and you want to try them out.&lt;/p&gt;

&lt;p&gt;But unless you have a &lt;strong&gt;known issue&lt;/strong&gt; with the related process, you &lt;strong&gt;should not change it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You need to &lt;strong&gt;reach a consensus&lt;/strong&gt; among impacted teams before starting any work on a new workflow or its update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploration is fine&lt;/strong&gt; though. Necessary even, to understand what the community has to offer. &lt;br&gt;
See next point.&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;We use RFCs to have a transparent and open discussion about new technologies we want to use, or new workflows we want to implement.&lt;/p&gt;

&lt;p&gt;Having a document written down helps with the global vision of the change we're about to make. It reveals misplaced or incompatible workflows and edge cases. &lt;/p&gt;

&lt;p&gt;We're able to gather feedback from everyone involved and refine it along the way. To make it even better and more personalized.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  3. 🗺️ Keep exploring what the community has to offer.
&lt;/h2&gt;

&lt;p&gt;Keep a good exploration routine and a thorough tech watch. Using Hacker News, Reddit, X (Twitter), whichever you prefer. There is no single answer as long as you keep yourself up to date with what's happening in the community you're part of.&lt;/p&gt;

&lt;p&gt;
  🔎 More details
  &lt;blockquote&gt;
&lt;p&gt;When you want to try something new, do it in a controlled, sandboxed environment and process. &lt;br&gt;
Keep a written list of things you're improving, as well as shortcomings and potential maintenance costs along the way.&lt;/p&gt;

&lt;p&gt;You don't really have to go all in, it's really just to understand how it would translate to your area and the specific needs your platform has.&lt;/p&gt;

&lt;p&gt;Later down the line, you'll notice that you'll be able to connect problems and feedback with solutions you've seen during your watch.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  4. 💻 The technology chosen for a workflow should be known and understood by the people that use it the most.
&lt;/h2&gt;

&lt;p&gt;Workflows implemented for the frontend &lt;em&gt;should&lt;/em&gt; use JavaScript. For the backend, Python is used, etc…&lt;/p&gt;

&lt;p&gt;This allows the people that are the most impacted by the workflow to &lt;strong&gt;fix and tweak it&lt;/strong&gt; if needed.&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;We used to have a monorepo for both our frontend and backend.&lt;/p&gt;

&lt;p&gt;The infrastructure was orchestrated around a &lt;code&gt;Rakefile&lt;/code&gt; (Ruby) triggering Bash and Python scripts. No-one from the frontend teams wanted to dive into that.&lt;/p&gt;

&lt;p&gt;We've split the frontend in its own repository, and started to port everything to NodeJS. Making it more approachable for us and the other frontend engineers.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  5. 🦾 A workflow should be tightly related to the infrastructure it's applied to, its needs, and its context.
&lt;/h2&gt;

&lt;p&gt;You should not try to implement a workflow once and expect it to cover every problem in existence across unrelated platforms or infrastructures.&lt;/p&gt;

&lt;p&gt;A workflow should be implemented in the context of the infrastructure it's running on/over/in…&lt;/p&gt;

&lt;p&gt;If you aim too broad, you'll end up with a cluttered workflow that's loaded with unwanted overheads, slower and more complicated than needed. This will impact multiple engineers, everytime they use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You implement and test it once, they use it thousands of times everyday.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;Deployments used to be handled by a Bash script, written outside of our repository, which triggered a Go script, also in a different repository. &lt;/p&gt;

&lt;p&gt;This workflow and tooling was written to cover every need, for everyone, with many conditions and edge cases.&lt;/p&gt;

&lt;p&gt;Like the &lt;code&gt;Rakefile&lt;/code&gt; based infrastructure from the previous example, nobody wanted to touch these Bash or Go scripts. It was slow, but too difficult to really update without a risk of breaking deployment of other projects.&lt;/p&gt;

&lt;p&gt;We wrote our own deployment script with only what was needed for deploying our frontend. It's in NodeJS, versioned in our main repository, so everyone can tweak it as needed, and be aware of its changes. And it shares the same primitives as the rest of the company, so anyone from outside can still interact with it.&lt;/p&gt;

&lt;p&gt;The overall workflow didn't change, meaning that from the engineer's point of view, nothing changed. The process is now 10 times faster and our frontend engineers are able to change what is uploaded or not simply by changing some JS code they are already familiar with.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  6. 📣 Any new or updated workflow should be transparently communicated at large.
&lt;/h2&gt;

&lt;p&gt;Too much is better than not enough (&lt;em&gt;in this context&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;When you finally have a go at a new workflow or updating an old one, it is very important to communicate at every step of the process to every one impacted by it.&lt;/p&gt;

&lt;p&gt;
  🔎 More details
  &lt;blockquote&gt;
&lt;p&gt;You start with a presentation of the whole project before the first line of code is even written. You explain &lt;strong&gt;why and how&lt;/strong&gt; you do it, with an overall approximative timeline (can also be done through an RFC).&lt;/p&gt;

&lt;p&gt;Then, once you've started working on it, you regularly report progress, clarify the timeline and list any required actions at every step of the migration (if necessary), this can be done by mail.&lt;/p&gt;

&lt;p&gt;Finally, at completion, you explain again the new/updated workflow, but also reflect on what went well and what could have been done better, this can be done by mail and presentations.&lt;/p&gt;

&lt;p&gt;This helps other engineers see your work. Enforcing the idea that you're not just a support team, but that you care about &lt;strong&gt;their happiness&lt;/strong&gt; and you act on improving it every day of the week.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  7. 🏷️ You own the platform, they own its use cases.
&lt;/h2&gt;

&lt;p&gt;When creating a new tool or trying to find a new solution, it's important to think about ownership from the start.&lt;/p&gt;

&lt;p&gt;Whatever solution you'll need to implement will end up being used by tens, hundreds, or, if you're very lucky, THOUSANDS of engineers.&lt;br&gt;
You have to think of it as a platform from the beginning, a platform to which anyone could plug their very own tweak to it.&lt;br&gt;
Tweaks that will ultimately be owned by them, meaning that any bug, fix or modification will be handled by them. Meanwhile, you can focus on the platform and the glue that ties everything together.&lt;/p&gt;

&lt;p&gt;Ownership of these additions have to be very clearly defined and more importantly enforced. Either by documentation or even better, with a &lt;a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners" rel="noopener noreferrer"&gt;CODEOWNERS file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to that, contributing to that platform should be clearly documented, and if possible automated as much as possible. If you want people to help you, &lt;strong&gt;you have to help them do it in the most frictionless way possible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;We have a CLI platform in our frontend repository that we use for any tooling need. It goes from printing some information from the CI/CD to uploading our assets in production.&lt;/p&gt;

&lt;p&gt;Anyone from anywhere, as long as they know how to write TypeScript, can contribute. All they have to do is run a CLI command to bootstrap their very own command. It will create the files, add them to the CODEOWNERS file and through comments in the code give some good practices and guidelines.&lt;/p&gt;

&lt;p&gt;We've grown it to more than a hundred commands, all of them documented, fully owned and easily accessible from our repository.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  8. 📋 Gather feedback periodically, if not continuously.
&lt;/h2&gt;

&lt;p&gt;It is very important to keep yourself very aware of what happens in the community you're helping.&lt;br&gt;
You need to understand how the platform is used everyday by your engineers. &lt;/p&gt;

&lt;p&gt;There are a few ways to keep yourself in the game.&lt;/p&gt;

&lt;p&gt;You can conduct regular embeds, from your platform team into product teams, so you can experience first hand the DX for a given team.&lt;br&gt;
It means you'll work with them directly, as part of their team and following their processes.&lt;br&gt;
You still have to prepare for it, meet with the team's leads beforehand, explain the motivations and look for self-contained tasks that can be completed easily, as if they were done by a new team member.&lt;/p&gt;

&lt;p&gt;Another solution is to survey teams.&lt;br&gt;
There are different ways of doing this, the easy way is to have a form with questions.&lt;br&gt;
More complicated ways could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some questions integrated into common CLIs.&lt;/li&gt;
&lt;li&gt;Injected forms/popups in common tools' UI.&lt;/li&gt;
&lt;li&gt;Automated Slack questions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your imagination is the limit.&lt;/p&gt;

&lt;p&gt;A few tips for surveys though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Questions should be the same across iterations, so you can track progress. &lt;/li&gt;
&lt;li&gt;Questions should follow some kind of "framework" both for the phrasing and the meaning (like &lt;a href="https://queue.acm.org/detail.cfm?id=3454124" rel="noopener noreferrer"&gt;SPACE&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Remain high level, you'll go deeper later on if you need to.&lt;/li&gt;
&lt;li&gt;Keep the survey as short as possible.&lt;/li&gt;
&lt;li&gt;Organize the questions by theme.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it's time to end the survey, analyze the answers and identify patterns.&lt;br&gt;
Don't focus on the "one-off" comments, see the big picture.&lt;/p&gt;

&lt;p&gt;
  🔎 IRL Example
  &lt;blockquote&gt;
&lt;p&gt;We send a survey to all the frontend engineers twice a year.&lt;/p&gt;

&lt;p&gt;It's usually sent out a few weeks before the transition to the next quarter, so we can use the results to define our objectives for the next 6 months (it spreads over two quarters).&lt;/p&gt;

&lt;p&gt;We also brought in other platform teams so they can ask their own questions as well, so we keep a single survey, instead of having plenty of them and tiring our engineers with questions all year round.&lt;/p&gt;

&lt;p&gt;It really helped our team, drove it in the right direction, especially when I was alone in it, to better define what was the most problematic and which points were high value for a small cost.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;





&lt;p&gt;I hope this clears things up for you and will help you better manage the platform work at your company.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you have best practices regarding platform teams you'd like to share?
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Psssst... 🤫 &lt;a href="https://grnh.se/6ec99ca71us" rel="noopener noreferrer"&gt;we're hiring&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;ℹ️ This is a re-work of my previous article &lt;a href="https://dev.to/yoannmoinet/developer-experience-magna-carta-2eak"&gt;Developer Experience Magna Carta&lt;/a&gt; with more content.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;🌄 Photo by &lt;a href="https://unsplash.com/@max_duz?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Max Duzij&lt;/a&gt; on &lt;a href="https://unsplash.com/fr/photos/uomo-di-fronte-a-tre-monitor-di-computer-mentre-era-seduto-qAjJk-un3BI/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Thank you Erik for the thorough proofreading.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dx</category>
      <category>programming</category>
      <category>devops</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Developer Experience Magna Carta 📋</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 16 Feb 2021 15:32:13 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/developer-experience-magna-carta-2eak</link>
      <guid>https://dev.to/yoannmoinet/developer-experience-magna-carta-2eak</guid>
      <description>&lt;h3&gt;
  
  
  ℹ️ I've reworked this article in a different format with more content.
&lt;/h3&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/yoannmoinet" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51386%2F05ffc384-4b71-44dc-86ff-0d5eddccc382.jpg" alt="yoannmoinet"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/yoannmoinet/platform-teams-best-practices-30l1" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;🧑‍💻 Platform Teams best practices&lt;/h2&gt;
      &lt;h3&gt;Yoann Moinet ・ Nov 15 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#dx&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devrel&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Bon matin 👋
&lt;/h2&gt;

&lt;p&gt;I'm Yoann Moinet, a frenchman living in Montpellier.&lt;br&gt;
In 2019, I joined &lt;a href="https://datadoghq.com" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; and bootstrapped the &lt;strong&gt;Frontend Platform&lt;/strong&gt; team.&lt;/p&gt;

&lt;p&gt;But, what is the &lt;strong&gt;Frontend Platform&lt;/strong&gt; you ask?&lt;/p&gt;

&lt;p&gt;We're improving the developer experience and remove any pain points in the &lt;em&gt;day-to-day&lt;/em&gt; work of all the Frontend engineers at Datadog.&lt;br&gt;
We cover a lot of ground: build, tests, deployment, code health, internal tools, and more...&lt;/p&gt;

&lt;p&gt;Working in a &lt;strong&gt;large scale&lt;/strong&gt; environment, we had to come up with a &lt;strong&gt;charter&lt;/strong&gt; so we don't lose focus and try to fix everything at once: that would end up being a bad experience for everyone involved.&lt;/p&gt;

&lt;p&gt;Here is our charter:&lt;/p&gt;
&lt;h2&gt;
  
  
  1. 🏢 Workflows --not their implementation-- need to be shared company wide.
&lt;/h2&gt;



&lt;p&gt;We should share similar workflows between teams and technologies, so it's easy for newcomers or someone working on an incident to quickly get up to speed even in a different repository/project.&lt;/p&gt;

&lt;p&gt;Markers and primitives need to be identified in each workflow in order to keep them similar across implementations.&lt;/p&gt;

&lt;p&gt;These implementations &lt;strong&gt;can be different&lt;/strong&gt; as long as a team is there to own the support and follow the same identified markers/primitives.&lt;/p&gt;

&lt;p&gt;In the case where there is already an established tool foundation, we should have, at the very least, &lt;strong&gt;hooks/flexibility&lt;/strong&gt; to customize the workflow in order to align with the project's tech stack and complexity.&lt;/p&gt;

&lt;p&gt;
  🔎 Example
  &lt;blockquote&gt;
&lt;p&gt;We have a command to deploy to staging from whichever repo you work on. &lt;br&gt;
This command waits for the feature branch's pipeline to be 100% ✅ before triggering the deploy. &lt;/p&gt;

&lt;p&gt;For the Frontend, it's safe enough to deploy on staging at an earlier step in the pipeline, so we updated the global tool to only wait for a specific job in the CI before deploying. This way it can trigger much sooner for our Frontend.&lt;/p&gt;

&lt;p&gt;This alone reduced the staging workflow by half for our Frontend teams without impacting the global workflow itself.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  2. 💡 Workflows should not be created or changed unless it's tightly related to a known and documented problem.
&lt;/h2&gt;




&lt;p&gt;We may want to test new technologies or read articles about new workflows and we want to try them out.&lt;/p&gt;

&lt;p&gt;But unless we have a &lt;strong&gt;known issue&lt;/strong&gt; with the related process, we &lt;strong&gt;should not change it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We need to &lt;strong&gt;reach a consensus&lt;/strong&gt; among impacted teams before starting any work on a new workflow or its update.&lt;/p&gt;

&lt;p&gt;
  🔎 Example
  &lt;blockquote&gt;
&lt;p&gt;We use RFCs to have a transparent and open discussion about new technologies we want to use, or new workflows we want to implement.&lt;/p&gt;

&lt;p&gt;Having a document written down helps with the global vision of the change we're about to make. It reveals misplaced or incompatible workflows and edge cases. &lt;/p&gt;

&lt;p&gt;We're able to gather feedback from everyone involved or touched by it and refine it along the way. To make it even better and more personalised.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  3. 💻 The technology chosen for a workflow should be known and understood by the people that use it the most.
&lt;/h2&gt;




&lt;p&gt;Workflows implemented for the Frontend should use JavaScript. For the Backend, Python is used, etc…&lt;/p&gt;

&lt;p&gt;This allows the people that are the most impacted by the workflow to &lt;strong&gt;fix and tweak it&lt;/strong&gt; if needed.&lt;/p&gt;

&lt;p&gt;
  🔎 Example
  &lt;blockquote&gt;
&lt;p&gt;We used to have a monorepo for both our Frontend and Backend.&lt;/p&gt;

&lt;p&gt;The infrastructure was orchestrated around a &lt;code&gt;Rakefile&lt;/code&gt; (Ruby) triggering Bash and Python scripts. No-one from the Frontend teams wanted to dive into that.&lt;/p&gt;

&lt;p&gt;We've split the repository, and started to port everything to JS. Making it more approachable for us and the other Frontend engineers.&lt;/p&gt;

&lt;p&gt;We've created a new deployment platform for internal applications, written in JS. It is now used by more than 5 different teams, with 10 new internal applications, all of them maintained solely by Frontend engineers. &lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  4. 🦾 A workflow should be tightly related to the infrastructure it's applied to, its needs, and its context.
&lt;/h2&gt;




&lt;p&gt;We should not try to implement a workflow once and expect it to cover every problem in existence across unrelated platforms.&lt;/p&gt;

&lt;p&gt;A workflow should be implemented in the context of the infrastructure it's running on/over/in…&lt;/p&gt;

&lt;p&gt;If we aim too broad, we may end up with a cluttered workflow that's loaded &lt;strong&gt;with unwanted overheads, slower and more complicated than needed&lt;/strong&gt;. This can impact multiple engineers, everytime they use it.&lt;/p&gt;

&lt;p&gt;We implement and test it once, they use it a thousand times.&lt;/p&gt;

&lt;p&gt;
  🔎 Example
  &lt;blockquote&gt;
&lt;p&gt;Deployments used to be handled by a Bash script, written outside of our repository, which triggered a Go script, also in another repository. &lt;/p&gt;

&lt;p&gt;This workflow and tooling was written to cover every needs, for everyone, with many conditions and edge cases.&lt;/p&gt;

&lt;p&gt;Like the &lt;code&gt;Rakefile&lt;/code&gt; based infrastructure from the previous example, nobody wanted to touch these bash or go scripts. It was slow, but too difficult to really update without a risk of breaking deployment of other projects.&lt;/p&gt;

&lt;p&gt;We wrote our own deployment script with only what was needed for deploying our Frontend. It's in JS, hosted in our main repository, so everyone can tweak it as needed. &lt;/p&gt;

&lt;p&gt;The overall workflow didn't change, meaning that from the engineer's point of view, nothing changed. Just the process is now 10x faster and our Frontend engineers are able to change what is uploaded or not simply by changing JS code they are already familiar with.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  5. 📣 Any new or updated workflow should be communicated at large.
&lt;/h2&gt;




&lt;p&gt;Too much is better than not enough.&lt;/p&gt;

&lt;p&gt;When we finally have a go at a new or an old workflow, it is very important to communicate at every step of the process to every one impacted by it.&lt;/p&gt;

&lt;p&gt;We start with a presentation of the whole project before the first line of code is written. We explain &lt;strong&gt;why and how&lt;/strong&gt; we do it, with an overall timeline (can be done through an RFC).&lt;/p&gt;

&lt;p&gt;Then once we've started working on it, we show progress, give a clear timeline and any required actions at every step of the migration (this can be done by mail).&lt;/p&gt;

&lt;p&gt;Finally, at completion, we explain again the new/updated workflow, but also reflecting on what went well and what could have been done better (this can be done by mail and presentations).&lt;/p&gt;

&lt;p&gt;This helps other engineers see our work. Enforcing the idea that we're not just a support team, but that we care about &lt;strong&gt;their happiness&lt;/strong&gt; and we act on improving it every day of the week.&lt;/p&gt;




&lt;p&gt;I hope this clears things up for you and will help you manage the DX at your company.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you have best practices regarding the DX you'd like to share?
&lt;/h3&gt;




&lt;p&gt;Psssst... 🤫 &lt;a href="https://grnh.se/6ec99ca71us" rel="noopener noreferrer"&gt;we're hiring&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;🌄 Photo by &lt;a href="https://unsplash.com/@brookecagle?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Brooke Cagle&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Thank you Erik for the thorough proofreading.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dx</category>
      <category>devrel</category>
      <category>frontend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Electron on the App Store, Post release.</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 09 Feb 2021 21:41:19 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/electron-on-the-app-store-post-release-4d2d</link>
      <guid>https://dev.to/yoannmoinet/electron-on-the-app-store-post-release-4d2d</guid>
      <description>&lt;p&gt;This article is part of a 5 articles series about the publication of an Electron application into the Mac AppStore, &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fen%C3%AAt.re/" rel="noopener noreferrer"&gt;Fenêtre,&lt;/a&gt; &lt;code&gt;fənɛtʁ&lt;/code&gt;, lets you better multitask on your mac. It enables a &lt;a href="https://en.wikipedia.org/wiki/Picture-in-picture" rel="noopener noreferrer"&gt;picture-in-picture&lt;/a&gt; mode for any website/web-app, image, video or flat file.&lt;br&gt;
You can find the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre-lite/id1288451627" rel="noopener noreferrer"&gt;free version&lt;/a&gt;and the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre/id1286743037" rel="noopener noreferrer"&gt;paid version&lt;/a&gt; on the Mac App-Store.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Of course your app is perfect and has no bugs. But people need to be reassured, they need to be able to contact you, to congratulate you on your awesome work, on how you're a wonderful human being, and sometimes, tell you stuff about your mom.&lt;/p&gt;

&lt;h1&gt;
  
  
  Contact &amp;amp; feedback
&lt;/h1&gt;

&lt;p&gt;Having a line of communication with your customers is vital, a minimal setup involve having a very basic email redirection with your domain registrar. Having a &lt;a href="https://twitter.com/FenetreApp" rel="noopener noreferrer"&gt;Twitter account&lt;/a&gt; for the app helped a lot as well and I'm currently testing a &lt;a href="https://www.facebook.com/FenetreApp" rel="noopener noreferrer"&gt;Facebook page&lt;/a&gt;, we'll see where it goes.&lt;/p&gt;

&lt;p&gt;People tends to send valuable feedback, giving their take on the app and what would make their experience even better. It often opens up to great new ideas. When someone takes the time to give you feedback, it's constructive, most of the time. That's how I got to implement the setting to show/hide the dock icon, the size of the see-through mask, the context-menu, and more. Many, many, many bugs, that I never encountered before, were now reproducible with steps, and more importantly, fixed.&lt;/p&gt;

&lt;p&gt;But, we're not snowflakes ❄️, negative feedback is inevitable, just don't take it too personally, people don't even know you anyway. A part of the negative comments I got felt like jealousy more than anything else. It was always in the form of "Meh, I wouldn't have done it this way... it's useless anyway...", your level of vulgarity may vary of course.&lt;/p&gt;

&lt;p&gt;The other part was actually very constructive and helped me improve the app.&lt;/p&gt;

&lt;p&gt;Anyway, I just know &lt;strong&gt;for a fact&lt;/strong&gt; that I am a wonderful person, and so are you 🌷&lt;/p&gt;

&lt;h1&gt;
  
  
  A Bug's life
&lt;/h1&gt;

&lt;p&gt;It's never fun as a user to get an unexpected error pop up in your face. So the first thing to do in my opinion, when you're packaging your app is to deactivate them.&lt;/p&gt;

&lt;p&gt;For this I've used the awesome &lt;a href="https://github.com/sindresorhus/electron-unhandled" rel="noopener noreferrer"&gt;electron-unhandled&lt;/a&gt; which will intercept every exceptions of your app, both on the main process and the renderer process.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Block error popups in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's really easy to setup, and you'll be able to handle all your errors in one place. Note the use of our &lt;code&gt;IS_PRODUCTION&lt;/code&gt; global variable set in Webpack earlier, to completely remove any dialog related to errors.&lt;/p&gt;

&lt;p&gt;The only thing that you need to do is execute this as early as you can in both processes, main and renderer. I usually put it at the start of my &lt;code&gt;app.js&lt;/code&gt; and in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of my views.&lt;/p&gt;

&lt;p&gt;You still need to get a ping when that happen, don't you think? For this, you can easily setup a free account on &lt;a href="https://www.mailgun.com/" rel="noopener noreferrer"&gt;Mailgun&lt;/a&gt; which will let you send up to 10000 emails per month. If you ever need more than that for your app, it means that you're more than able to pay for it.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/bojand/mailgun-js" rel="noopener noreferrer"&gt;mailgun-js&lt;/a&gt; and a very &lt;a href="http://blog.mailgun.com/how-to-send-transactional-emails-in-a-nodejs-app-using-the-mailgun-api/" rel="noopener noreferrer"&gt;thorough tutorial&lt;/a&gt; written by Mailgun you'll be up and running in no time. Then, you can do something like that in your &lt;code&gt;unhandled&lt;/code&gt; configuration:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Send me an email instead of showing the error to the user.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This whole experience was very enlightening and taught me so much on every aspects of the application's ecosystem and life cycle on Mac OS X. From the very beginning of the conceptualization to the publishing into the AppStore, even after that, marketing and doing support. It was a blast, and still is, I'm very glad I was able to stay motivated all along.&lt;/p&gt;

&lt;p&gt;And it didn't even cost me that much (±130$) for a 10x return on investment after the first month. For which I'm very grateful, I never expected that.&lt;/p&gt;

&lt;p&gt;There were very hard times, but the most painful it became, the more enjoyable the finality was.&lt;/p&gt;

&lt;p&gt;I'd do it again anytime, and I'd still choose Electron for this job. But for a different app, less tied to a web-view, I'd definitely go with Swift, I'm not &lt;em&gt;that&lt;/em&gt; crazy.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed my writing, I don't do that often. But if this is well received, I might write some other stuffs.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>appstore</category>
      <category>menubar</category>
    </item>
    <item>
      <title>Electron on the App Store, Go to market.</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 09 Feb 2021 21:41:06 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/electron-on-the-app-store-go-to-market-3m1k</link>
      <guid>https://dev.to/yoannmoinet/electron-on-the-app-store-go-to-market-3m1k</guid>
      <description>&lt;p&gt;This article is part of a 5 articles series about the publication of an Electron application into the Mac AppStore, &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fen%C3%AAt.re/" rel="noopener noreferrer"&gt;Fenêtre,&lt;/a&gt; &lt;code&gt;fənɛtʁ&lt;/code&gt;, lets you better multitask on your mac. It enables a &lt;a href="https://en.wikipedia.org/wiki/Picture-in-picture" rel="noopener noreferrer"&gt;picture-in-picture&lt;/a&gt; mode for any website/web-app, image, video or flat file.&lt;br&gt;
You can find the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre-lite/id1288451627" rel="noopener noreferrer"&gt;free version&lt;/a&gt;and the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre/id1286743037" rel="noopener noreferrer"&gt;paid version&lt;/a&gt; on the Mac App-Store.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This is where you have to excel if you really want to sell your app. There is no "organic marketing/sharing/growing", at least not initially. You have to go out there, pull your fingers from your ass 👉 🍑 (&lt;em&gt;french saying, don't bother&lt;/em&gt;) and make your app look pretty and useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Make a video... or many
&lt;/h1&gt;

&lt;p&gt;You've been working on your app for half a year now. Of course it's filling a need, it's obvious... to you... and only you.&lt;/p&gt;

&lt;p&gt;People don't know they need it so badly. Sometimes, they don't even understand what it does.&lt;/p&gt;

&lt;p&gt;A great way to fix this is to make short videos, showing how it works.&lt;/p&gt;

&lt;p&gt;Here's what I did myself, I'm no video maker guru ninja, but at least it's showing some of the features at a glance and breaks the ice of the first use.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/mOuQyzbHzO4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Another way is to share real-life usage. People need to see themselves using your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft4rvv38fo1ot64vyy7c4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft4rvv38fo1ot64vyy7c4.png" alt="image" width="800" height="177"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Multiple points of entry
&lt;/h1&gt;

&lt;p&gt;You have to share your amazing creation and create as many entry points as possible. For it, you'll have to submit it to a big number of platforms. And more importantly, always share promo codes, you have 100 of them for each version of your app, so don't be cheap with them.&lt;/p&gt;

&lt;p&gt;The way I see it, there are three types of channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ephemeral&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where a lot of content is shared everyday, and it's organically and automatically curated by a great number of users. Big amount of content, big audience, very fast content rotation and very small attention span.&lt;/p&gt;

&lt;p&gt;When sharing there, you'll have to be straight to the point with very explicit visuals. Show what it does and how it does it, limit your post to only the greatest/fanciest characteristics.&lt;/p&gt;

&lt;p&gt;Be careful to sync your post with the timezone of your principal target as well, otherwise it will be drowned very quickly.&lt;/p&gt;

&lt;p&gt;Here are many great tips for posting on &lt;a href="https://blog.producthunt.com/how-to-launch-on-product-hunt-7c1843e06399" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example of ephemeral platforms: &lt;a href="https://news.ycombinator.com/item?id=15410725" rel="noopener noreferrer"&gt;HackerNews&lt;/a&gt;, &lt;a href="https://www.producthunt.com/posts/fenetre" rel="noopener noreferrer"&gt;ProductHunt&lt;/a&gt;, specific subreddits like &lt;a href="https://www.reddit.com/r/apple/comments/74vyca/i_made_a_mac_app_to_let_you_have_a_window_in/" rel="noopener noreferrer"&gt;/r/apple&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/macapps/comments/74vw47/fen%C3%AAtre_picture_in_picture_for_your_mac/" rel="noopener noreferrer"&gt;/r/macapps&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/appdev" rel="noopener noreferrer"&gt;/r/appdev&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/app" rel="noopener noreferrer"&gt;/r/app&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/startups" rel="noopener noreferrer"&gt;/r/startups&lt;/a&gt;, &lt;a href="https://reddit.com/r/entrepreneur" rel="noopener noreferrer"&gt;/r/entrepreneur&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/sideproject" rel="noopener noreferrer"&gt;/r/sideproject&lt;/a&gt;...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Temporary&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another less fast paced platform would be blog posts and videos. Small amount of content and big audience. There is a rotation of content but way slower than ephemeral platforms.&lt;/p&gt;

&lt;p&gt;You have three possibilities here. Either your app gets published naturally when a blogger finds it via another channel. Or you'll send it yourself to as many tech blog as you can and get lucky. Or you'll write the article yourself, or five of them 😘.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Durable&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a highly specialized content but small audience (at first), no rotation of content and great attention span, you have your own app's website/blog, advertising, as long as you pay for it, or partnerships with other services/products.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show them what they're missing out
&lt;/h1&gt;

&lt;p&gt;Free users have a limited set of features, but still get to see the settings window. It is a great opportunity to show them what they could get once they decide to upgrade.&lt;/p&gt;

&lt;p&gt;Here's the settings of the pro version, you can change default behaviors and set your own hotkeys.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbykdvn7w5ph5748frrx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbykdvn7w5ph5748frrx4.png" alt="image" width="800" height="420"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;So here's what I first tried, a very light, subtle hint about the pro features:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpu31gh40horq5zskao0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpu31gh40horq5zskao0o.png" alt="image" width="800" height="420"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Needless to say, this got rejected by Apple faster than I was rejected in high school. But Apple did let me have this instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhhh06bmqeajoc24uqwmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhhh06bmqeajoc24uqwmq.png" alt="image" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, don't be too obvious. Subtlety seems to work the best (should have known that in high school).&lt;/p&gt;




&lt;p&gt;Everything is now in place to sell this app, but what about its after-life? What can we do &lt;a href="https://dev.to/yoannmoinet/electron-on-the-app-store-post-release-4d2d"&gt;Post release&lt;/a&gt;?&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>appstore</category>
      <category>menubar</category>
    </item>
    <item>
      <title>Electron on the App Store, Ship it.</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 09 Feb 2021 21:40:51 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/electron-on-the-app-store-ship-it-4mf0</link>
      <guid>https://dev.to/yoannmoinet/electron-on-the-app-store-ship-it-4mf0</guid>
      <description>&lt;p&gt;This article is part of a 5 articles series about the publication of an Electron application into the Mac AppStore, &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fen%C3%AAt.re/" rel="noopener noreferrer"&gt;Fenêtre,&lt;/a&gt; &lt;code&gt;fənɛtʁ&lt;/code&gt;, lets you better multitask on your mac. It enables a &lt;a href="https://en.wikipedia.org/wiki/Picture-in-picture" rel="noopener noreferrer"&gt;picture-in-picture&lt;/a&gt; mode for any website/web-app, image, video or flat file.&lt;br&gt;
You can find the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre-lite/id1288451627" rel="noopener noreferrer"&gt;free version&lt;/a&gt;and the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre/id1286743037" rel="noopener noreferrer"&gt;paid version&lt;/a&gt; on the Mac App-Store.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This is the last mile, and also where I almost lost it. By far the most painful part of the whole project.&lt;/p&gt;

&lt;p&gt;Working with Electron, you won't publish, sign or handle entitlements from XCode, and Apple seems to make it even harder for you since you're not using their proprietary software. The documentation is cryptic, to say the least, and the support is yet to be found.&lt;/p&gt;

&lt;p&gt;What helped me a lot was the &lt;a href="https://github.com/webtorrent/webtorrent-desktop" rel="noopener noreferrer"&gt;WebTorrent for desktop repo,&lt;/a&gt; especially their &lt;a href="https://github.com/webtorrent/webtorrent-desktop/blob/master/bin/package.js" rel="noopener noreferrer"&gt;packaging script&lt;/a&gt;, so, a &lt;strong&gt;very big thank you&lt;/strong&gt; to the team there ❤️, your project saved my sanity, for real 🙏.&lt;/p&gt;

&lt;p&gt;Following their great example I've been successfully using &lt;a href="https://github.com/electron-userland/electron-packager" rel="noopener noreferrer"&gt;electron-packager&lt;/a&gt; and &lt;a href="https://github.com/electron-userland/electron-osx-sign" rel="noopener noreferrer"&gt;electron-osx-sign&lt;/a&gt; which are must-have packages to ship your product to the AppStore without XCode.&lt;/p&gt;

&lt;h1&gt;
  
  
  Certificates &amp;amp; stuffs
&lt;/h1&gt;

&lt;p&gt;First of all, you'll need 3 different &lt;strong&gt;certificates&lt;/strong&gt; from &lt;a href="https://developer.apple.com/account/mac/certificate/" rel="noopener noreferrer"&gt;your developer account&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mac development&lt;/strong&gt;, so you can test your signed packaged app before sending it to Apple.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac AppStore → Mac App Distribution&lt;/strong&gt;, so you can sign your app and all executable inside it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac AppStore → Mac Installer Distribution&lt;/strong&gt;, so you can sign the package of your app, which is the format you'll send to the AppStore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply download them, double-click and they will be installed in your keychain. That's it, you won't have to deal with these anymore until they expire.&lt;/p&gt;

&lt;p&gt;Then you'll need to create &lt;a href="https://developer.apple.com/account/mac/identifier/bundle" rel="noopener noreferrer"&gt;an App ID&lt;/a&gt; for your app (one for each version, free and pro), and finally, two &lt;a href="https://developer.apple.com/account/mac/profile/" rel="noopener noreferrer"&gt;provisioning profiles&lt;/a&gt; each, one for development and one for distribution.&lt;/p&gt;

&lt;p&gt;Now you're ready to sign your app.&lt;/p&gt;

&lt;h1&gt;
  
  
  Entitlements.plist
&lt;/h1&gt;

&lt;p&gt;Using &lt;a href="https://github.com/TooTallNate/plist.js" rel="noopener noreferrer"&gt;plist.js&lt;/a&gt; you can create your two entitlement files needed, &lt;code&gt;parent.plist&lt;/code&gt; and &lt;code&gt;child.plist&lt;/code&gt;. I like doing it in the packaging script, so I don't end up with multiple files I'll never edit.&lt;/p&gt;

&lt;p&gt;The parent will need &lt;code&gt;com.apple.security.sand-box&lt;/code&gt; at &lt;code&gt;true&lt;/code&gt;, because your app _has_to be sand-boxed before being submitted to the AppStore. And &lt;code&gt;com.apple.application-identifier&lt;/code&gt; and &lt;code&gt;com.apple.developer.team-identifier&lt;/code&gt; to identify your app.&lt;/p&gt;

&lt;p&gt;Add any &lt;a href="https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html" rel="noopener noreferrer"&gt;other needed entitlements&lt;/a&gt; depending on what your application does.&lt;/p&gt;

&lt;p&gt;As an example, for &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt; I'm also using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;com.apple.security.network.client&lt;/code&gt; since we're connecting to the internet when displaying a website or any URL in Fenêtre.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.apple.security.network.server&lt;/code&gt; for the browser extension, so it can send URLs to open. It's also used when encoding/decoding unsupported video formats.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.apple.security.files.user-selected.read-write&lt;/code&gt; for the user to drag-n drop files into the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;code&gt;child.plist&lt;/code&gt; it's quite easier, you just need &lt;code&gt;com.apple.security.sand-box&lt;/code&gt; and &lt;code&gt;com.apple.security.inherit&lt;/code&gt; at &lt;code&gt;true&lt;/code&gt;. That's it, that's all, output those file somewhere temporary, we'll use them later during the signature process.&lt;/p&gt;

&lt;h1&gt;
  
  
  Signature
&lt;/h1&gt;

&lt;p&gt;Now to the big tricky part, the signing of the app. Fortunately for us &lt;a href="https://github.com/electron-userland/electron-osx-sign" rel="noopener noreferrer"&gt;electron-osx-sign&lt;/a&gt; has done most of the hard part. Here's a snippet of my &lt;strong&gt;production&lt;/strong&gt; signature script:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Signature script for production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only two arguments needed, &lt;code&gt;appPath&lt;/code&gt; and &lt;code&gt;pkgPath&lt;/code&gt; , are the &lt;code&gt;.app&lt;/code&gt;'s absolute path we've created with &lt;a href="https://github.com/electron-userland/electron-packager" rel="noopener noreferrer"&gt;electron-packager&lt;/a&gt; and the absolute path of where we're outputting the &lt;code&gt;.pkg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note the &lt;code&gt;PARENT_PLIST_PATH&lt;/code&gt; and &lt;code&gt;CHILD_PLIST_PATH&lt;/code&gt; that we also created earlier. And the two different identities we're using, one for the application itself, and another one for the &lt;code&gt;.pkg&lt;/code&gt; we're generating. And the &lt;code&gt;platform&lt;/code&gt; is &lt;code&gt;mas&lt;/code&gt; for 'Mac AppStore'.&lt;/p&gt;

&lt;p&gt;Now this package won't be testable on your machine, not even the signed app. Instead, you'll have to use another sign script for being able to test your sand-boxed application:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Signature script for development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that we don't need the generation of the &lt;code&gt;.pkg&lt;/code&gt; anymore, since we won't test it. Also, the &lt;code&gt;type&lt;/code&gt; changes to &lt;code&gt;development&lt;/code&gt;, and the identity is now the &lt;code&gt;Mac Developer&lt;/code&gt; one. As well as the &lt;code&gt;DEV_PROVISIONING_PROFILE&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Debug your sandboxed Application
&lt;/h1&gt;

&lt;p&gt;You now have the exact same app as the one that will be shipped to Apple. So you can test the sand-boxing, which is &lt;em&gt;very&lt;/em&gt; important and &lt;em&gt;very&lt;/em&gt; likely to crash.&lt;/p&gt;

&lt;p&gt;Be sure that everything works in this state, or it will be rejected by Apple right away. You can use the very useful &lt;a href="https://itunes.apple.com/fr/app/rb-app-checker-lite/id519421117?mt=12" rel="noopener noreferrer"&gt;RB App Checker Lite&lt;/a&gt; as well, to test the entitlements and the signature of your app.&lt;/p&gt;

&lt;p&gt;There's still this ' &lt;em&gt;The profile does NOT match the application's Team ID&lt;/em&gt;' I can't get rid of. But apparently, &lt;a href="https://github.com/electron-userland/electron-osx-sign/issues/149" rel="noopener noreferrer"&gt;this is not a big deal&lt;/a&gt; ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯.&lt;/p&gt;

&lt;p&gt;If you need to see the output of your main process, get into the package contents of your generated &lt;code&gt;.app&lt;/code&gt; and open the file located at &lt;code&gt;MyApp.app/Contents/MacOS/MyApp&lt;/code&gt;, it will open a terminal window with the output of your main process.&lt;/p&gt;

&lt;p&gt;To inspect the renderer process, simply spawn the web inspector from your &lt;code&gt;BrowserWindow&lt;/code&gt;'s options with the help of our global &lt;code&gt;IS_PRODUCTION&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;p&gt;Now that you're all set, you can use the Application Loader to upload your &lt;code&gt;Application.pkg&lt;/code&gt; to your &lt;a href="https://itunesconnect.apple.com/" rel="noopener noreferrer"&gt;iTunes Connect&lt;/a&gt; account.&lt;/p&gt;

&lt;p&gt;It's hidden in &lt;code&gt;XCode &amp;gt; Open Developer Tool &amp;gt; Application Loader&lt;/code&gt;. Just open it once, and pin it to your dock, so you don't have to launch XCode each time you need to upload your app, which will happen a lot.&lt;/p&gt;

&lt;p&gt;Then from iTunes Connect you'll be able to select your latest build for the next release.&lt;/p&gt;

&lt;p&gt;Wait for the review to happen... update your app, and back to the start, until it's validated and pushed to the AppStore.&lt;/p&gt;




&lt;p&gt;Now that we have our package uploaded and published, we're ready to &lt;a href="https://dev.to/yoannmoinet/electron-on-the-app-store-go-to-market-3m1k"&gt;Go to market&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>appstore</category>
      <category>menubar</category>
    </item>
    <item>
      <title>Electron on the App Store, Pain &amp; tears.</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 09 Feb 2021 21:40:41 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/electron-on-the-app-store-pain-tears-nce</link>
      <guid>https://dev.to/yoannmoinet/electron-on-the-app-store-pain-tears-nce</guid>
      <description>&lt;p&gt;This article is part of a 5 articles series about the publication of an Electron application into the Mac AppStore, &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fen%C3%AAt.re/" rel="noopener noreferrer"&gt;Fenêtre,&lt;/a&gt; &lt;code&gt;fənɛtʁ&lt;/code&gt;, lets you better multitask on your mac. It enables a &lt;a href="https://en.wikipedia.org/wiki/Picture-in-picture" rel="noopener noreferrer"&gt;picture-in-picture&lt;/a&gt; mode for any website/web-app, image, video or flat file.&lt;br&gt;
You can find the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre-lite/id1288451627" rel="noopener noreferrer"&gt;free version&lt;/a&gt;and the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre/id1286743037" rel="noopener noreferrer"&gt;paid version&lt;/a&gt; on the Mac App-Store.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Development usually comes with some pain, this is not new, but I'll try to cover what was particularly hard to fix and/or to find a solution for.&lt;/p&gt;

&lt;h1&gt;
  
  
  Custom schemes
&lt;/h1&gt;

&lt;p&gt;I wanted to use the custom scheme &lt;code&gt;fenetre://&lt;/code&gt;to open links from the browser into the app. It seemed so easy following &lt;a href="https://github.com/electron/electron/blob/master/docs/api/app.md#appsetasdefaultprotocolclientprotocol-path-args" rel="noopener noreferrer"&gt;Electron's&lt;/a&gt; and &lt;a href="https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115" rel="noopener noreferrer"&gt;Apple's&lt;/a&gt; documentation on the subject. And it worked flawlessly &lt;em&gt;in development&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Once sand-boxed, it stopped working. And it wasn't an easy found bug, since it took 3 fully published and reviewed versions to figure this one out.&lt;/p&gt;

&lt;p&gt;You could accurately follow my descent into the abyss through the &lt;a href="https://twitter.com/FenetreApp" rel="noopener noreferrer"&gt;@FenetreApp&lt;/a&gt; twitter feed.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-917662600401506305-519" src="https://platform.twitter.com/embed/Tweet.html?id=917662600401506305"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-917662600401506305-519');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=917662600401506305&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-917883630243983361-981" src="https://platform.twitter.com/embed/Tweet.html?id=917883630243983361"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-917883630243983361-981');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=917883630243983361&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But, eventually... in the end.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-918254037467910144-800" src="https://platform.twitter.com/embed/Tweet.html?id=918254037467910144"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-918254037467910144-800');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=918254037467910144&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Instead of using a custom scheme, I had to run a server in the app, on a specified port. Then, the browser extension would call a route on this server to open the URL, passed as argument, into the app 🤮.&lt;/p&gt;

&lt;p&gt;And I hate this so much.&lt;/p&gt;

&lt;h1&gt;
  
  
  DRM Content
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;EDIT:&lt;/em&gt; This is now impossible to mock. You'll have to use a proprietary tool to publish your app in order to hope serving DRM content.&lt;/p&gt;

&lt;p&gt;When delivering content from the web, especially videos, you'll be hit in the face with DRM. Netflix, for example, won't let you play videos anywhere you like. You need a decoding plugin, called &lt;a href="http://www.widevine.com/" rel="noopener noreferrer"&gt;Widevine&lt;/a&gt;. It's already embedded in your day-to-day browser, but when you're using Chromium (Electron's core) you'll need to get it yourself.&lt;/p&gt;

&lt;p&gt;The best way is to look for &lt;a href="https://github.com/electron/electron/blob/master/docs/tutorial/using-widevine-cdm-plugin.md" rel="noopener noreferrer"&gt;the Chromium's major version your current Electron uses&lt;/a&gt; via &lt;code&gt;process.versions&lt;/code&gt; in the renderer process. Then download the &lt;a href="https://www.slimjet.com/chrome/google-chrome-old-version.php" rel="noopener noreferrer"&gt;same version of Chrome&lt;/a&gt; and go spelunking into the &lt;code&gt;.app&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;At the time of this writing, it can be found here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google Chrome.app/Contents/Versions/[version]/Google Chrome Framework.framework/Versions/A/Libraries/WidevineCdm/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, activate it in your app, as early as you can, before &lt;code&gt;app.on('ready')&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can do the same with the PepperFlash plugin, to read flash content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Important notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to be updated alongside Electron.&lt;/li&gt;
&lt;li&gt;to be manually copied into your package.&lt;/li&gt;
&lt;li&gt;to be referenced as an absolute path.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The French tartine de caca
&lt;/h1&gt;

&lt;p&gt;Since I'm French, I wanted something that sounded French. That's where this &lt;code&gt;ê&lt;/code&gt; came in, busting everything I did.&lt;/p&gt;

&lt;p&gt;Fenêtre was a fun name, pronounced &lt;code&gt;fənɛtʁ&lt;/code&gt; or &lt;em&gt;Fonaytre&lt;/em&gt;, it means window in French, so it was very relevant to the project and it sounded &lt;em&gt;putain de&lt;/em&gt; French. But nothing prepared me to how painful it would be to use a non-ASCII character in today's internet. I already knew it was stupid, but not &lt;em&gt;that&lt;/em&gt; stupid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;APFS vs HFS+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some time during the development, I decided to upgrade my machine to High Sierra, what a mistake that was.&lt;/p&gt;

&lt;p&gt;The file system changed &lt;a href="https://eclecticlight.co/2017/07/05/high-sierra-and-filenames-apple-is-relenting/" rel="noopener noreferrer"&gt;from HFS+ to APFS&lt;/a&gt;, and now, the system doesn't normalize filenames like it used to. So if you have non-ASCII characters in your filenames, &lt;a href="https://eclecticlight.co/2017/04/06/apfs-is-currently-unusable-with-most-non-english-languages/" rel="noopener noreferrer"&gt;you might be fucked&lt;/a&gt;. I could not sign my app with &lt;code&gt;codesign&lt;/code&gt; through &lt;a href="https://github.com/electron-userland/electron-osx-sign" rel="noopener noreferrer"&gt;electron-osx-sign&lt;/a&gt; for a few days before finding a solution.&lt;/p&gt;

&lt;p&gt;The solution I've found, with the help of &lt;a href="https://github.com/sethlu" rel="noopener noreferrer"&gt;Zhuo Lu,&lt;/a&gt; was to &lt;a href="https://github.com/electron-userland/electron-osx-sign/issues/155" rel="noopener noreferrer"&gt;get the name from the Finder and copy the special character from there to use it where needed in the code&lt;/a&gt;. Simply because I'm not that well versed in normalization matters, it was an easy enough way of fixing this annoyance once and for all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain Name&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Internationalized_domain_name" rel="noopener noreferrer"&gt;Internationalized domain name&lt;/a&gt; are around for some time now. You'd think it should be well supported all around the internet... BOOM, wake up, it's not.&lt;/p&gt;

&lt;p&gt;First, in most forms where you have to enter a domain name, you won't be able to use the special form &lt;code&gt;fenêt.re&lt;/code&gt;, it will be rejected by the validation, instead you'll have to use the &lt;code&gt;xn--fent-ipa.re&lt;/code&gt; form. So, developers, please update your validations so I can submit my website in its best form.&lt;/p&gt;

&lt;p&gt;Second, now that it passes the form validation, it will be displayed either badly, without the special char like &lt;code&gt;fent.re&lt;/code&gt;, or simply will be swapped back to the &lt;code&gt;xn--fent-ipa.re&lt;/code&gt; form.&lt;/p&gt;

&lt;p&gt;Third, it won't always be recognized to fetch open-graph data and you might not get this fancy card with your website's name/description/visual.&lt;/p&gt;

&lt;p&gt;Don't think it's just small, underground platforms that don't support it yet. It happened on ProductHunt, Google Chrome WebStore, CloudFront, Twitter, Facebook, Slack, to name a few and it really doesn't help the internationalization of domain names.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keyboards&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one is just minimal, and nothing can be done for this, I think. But some keyboards make it very difficult to type special characters, especially the US one. That's why I also bought the &lt;code&gt;getfenet.re&lt;/code&gt; domain.&lt;/p&gt;

&lt;p&gt;Small tips on how to type special characters on a &lt;em&gt;US International — PC&lt;/em&gt; layout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'&lt;/code&gt; then &lt;code&gt;e&lt;/code&gt; = &lt;code&gt;é&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;`&lt;/code&gt; then &lt;code&gt;a&lt;/code&gt; = &lt;code&gt;à&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift + 6&lt;/code&gt; then &lt;code&gt;e&lt;/code&gt; = &lt;code&gt;ê&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"&lt;/code&gt; then &lt;code&gt;i&lt;/code&gt; = &lt;code&gt;ï&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'&lt;/code&gt; then &lt;code&gt;c&lt;/code&gt; = &lt;code&gt;ç&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, you can combine the accent with many other letters.&lt;/p&gt;

&lt;h1&gt;
  
  
  Clipboard Watch
&lt;/h1&gt;

&lt;p&gt;There is no event for the clipboard in Electron (Chromium), so you'll need to watch it yourself. And if you're using a &lt;code&gt;setInterval&lt;/code&gt; for this, &lt;a href="https://github.com/electron/electron/issues/4465" rel="noopener noreferrer"&gt;you'll see it slowly dying with your inactive app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://github.com/electron/electron/blob/master/docs/api/power-save-blocker.md" rel="noopener noreferrer"&gt;&lt;code&gt;powerSaveBlocker&lt;/code&gt;&lt;/a&gt; enters.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Destroyed BrowserWindow
&lt;/h1&gt;

&lt;p&gt;When manipulating or doing stuff with an opened &lt;code&gt;BrowserWindow&lt;/code&gt;, be very careful that it's still alive, especially if it's asynchronous.&lt;/p&gt;

&lt;p&gt;Or you'll get hit by an exception.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Use this in every asynchronous piece of code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Transparent Windows
&lt;/h1&gt;

&lt;p&gt;I wanted to implement a see-through feature, being able to keep the window in front, but the cursor would cut through it to reveal what's behind. And let the user click through it as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2k5v0k5os485nt43zp89.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2k5v0k5os485nt43zp89.gif" alt="demo" width="560" height="315"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It was even easier than what I first thought (&lt;em&gt;or I was just being an idiot&lt;/em&gt;), it's actually just a combination of &lt;code&gt;BrowserWindow&lt;/code&gt;'s configurations and some CSS sorcery 🧙‍️:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Main and Renderer process code for see-through feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Using the app as a MacOS service
&lt;/h1&gt;

&lt;p&gt;In my journey to make this app the most deeply integrated into the OS as possible, I wanted to have it registered as &lt;a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/SysServices/Articles/overview.html#//apple_ref/doc/uid/20000850-97688" rel="noopener noreferrer"&gt;a MacOS service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, Electron's team doesn't find it &lt;a href="https://github.com/electron/electron/issues/8394" rel="noopener noreferrer"&gt;important enough to put it in the core&lt;/a&gt; (yet?).&lt;/p&gt;

&lt;p&gt;Which is a shame, or maybe, just not enough people care about it yet.&lt;/p&gt;

&lt;p&gt;Next step will be to implement a native Node module I guess.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reducing package size
&lt;/h1&gt;

&lt;p&gt;When shipping Electron with your app, you're getting a pretty huge deal of a package. Electron alone will add ~117MB to your package 🏋️‍♀️. So the more you'll remove, the better.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Webpack&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good way to have a smaller sized bundle would be to have a build system. I've chosen Webpack, because I'm familiar with it. But any other would have worked of course. Grunt, Gulp or any basic concatenation of files (&lt;em&gt;if you're that barbaric&lt;/em&gt;)...&lt;/p&gt;

&lt;p&gt;Webpack lets you &lt;a href="https://webpack.js.org/configuration/target/#string" rel="noopener noreferrer"&gt;target both &lt;code&gt;electron-main&lt;/code&gt;&lt;/a&gt; &lt;a href="https://webpack.js.org/configuration/target/#string" rel="noopener noreferrer"&gt;and &lt;code&gt;electron-renderer&lt;/code&gt;&lt;/a&gt;. This way, with only one &lt;code&gt;webpack.config.js&lt;/code&gt; you can output both your main process and your renderer process.&lt;/p&gt;

&lt;p&gt;Going deeper with webpack, you can declare globals thanks to the &lt;a href="https://webpack.js.org/plugins/define-plugin/" rel="noopener noreferrer"&gt;&lt;code&gt;DefinePlugin&lt;/code&gt;&lt;/a&gt; built in.&lt;/p&gt;

&lt;p&gt;And, if you need to use absolute paths from within your app's package using node's &lt;code&gt;path&lt;/code&gt; (for plugins for example), you should deactivate &lt;a href="https://webpack.js.org/configuration/node/#node-__dirname" rel="noopener noreferrer"&gt;webpack's &lt;code&gt;__dirname&lt;/code&gt; mock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of my webpack's configuration:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Snippet of my webpack config. Showing dual output, _dirname obfuscation and globals definition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then run &lt;code&gt;webpack --env.IS_PRO --env.IS_PROD --env.IS_PACKAGED&lt;/code&gt; depending on which build you need to create.&lt;/p&gt;

&lt;p&gt;Having those globals helped considerably keeping a single codebase with different codepaths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IS_PACKAGED&lt;/code&gt; : helped with the declaration of absolute paths. For plugins for example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IS_PROD&lt;/code&gt;: helped with adding debug points and debugger only in development.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IS_PRO&lt;/code&gt;: helped with obfuscating pro features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Closing tip. Register all your dependencies as a &lt;code&gt;devDependency&lt;/code&gt; will help with the packaging. Using electron-packager it will completely discard your &lt;code&gt;node_modules&lt;/code&gt; folder and only keep your bundled JavaScript when packaging your app, reducing the size considerably.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Languages&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flijxl00qypr64nqwk349.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flijxl00qypr64nqwk349.png" alt="image" width="800" height="413"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Without cleaning, you'll end up having all languages listed on your app's page.&lt;/p&gt;

&lt;p&gt;Electron adds a &lt;code&gt;.lproj&lt;/code&gt; folder for each supported language, &lt;a href="https://github.com/electron/electron/pull/363" rel="noopener noreferrer"&gt;for&lt;/a&gt; &lt;a href="https://github.com/electron-userland/electron-builder/issues/708" rel="noopener noreferrer"&gt;reasons&lt;/a&gt;. It will clutter your application's page on the Mac AppStore and will communicate wrong information about your app being internationalized in all these languages.&lt;/p&gt;

&lt;p&gt;You can remove them yourself &lt;strong&gt;after the packaging of your app&lt;/strong&gt;. To only keep the ones you support:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Icons
&lt;/h1&gt;

&lt;p&gt;When you iterate on your designs, you might need to update your icons quite a lot. And generating those can be a pain, since you need _many_size and format. Especially this &lt;code&gt;icon.icns&lt;/code&gt; for which many apps can ask &lt;strong&gt;up to 5$&lt;/strong&gt; to generate.&lt;/p&gt;

&lt;p&gt;To ease this process, I've used this script coming from this awesome &lt;a href="https://stackoverflow.com/a/20703594/488325" rel="noopener noreferrer"&gt;SO answer&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Here's the 5$ script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, just use it as &lt;code&gt;./icons.sh &amp;lt;input_file&amp;gt; &amp;lt;output_folder&amp;gt;&lt;/code&gt;, it is important to note that your input file _must_be at least 1024px in both directions.&lt;/p&gt;

&lt;p&gt;If you need to upscale it to a 1024px square, you can use &lt;a href="https://www.imagemagick.org/script/index.php" rel="noopener noreferrer"&gt;ImageMagick&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Unsupported videos
&lt;/h1&gt;

&lt;p&gt;Chromium only support a small set of video format. Mostly mp4 and its derivatives. So if a user wants to play an &lt;code&gt;.avi&lt;/code&gt; video, it won't work, because it doesn't work in Chromium... bummer.&lt;/p&gt;

&lt;p&gt;Since I'm just using a basic &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag to load all local videos, I'm stuck with this. Except... that's my app, and I can do whatever the fuck I want, if I want to support more video types, I will, try and stop me.&lt;/p&gt;

&lt;p&gt;Fortunately for us, we can listen to errors on the video, and even luckier for us, we can target missing support errors:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;From there, in &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;, I'm sending a ping back to the main process saying that I can't support this video type. The local server will create a new route for this video file and decode it on the fly using &lt;a href="https://github.com/fluent-ffmpeg/node-fluent-ffmpeg" rel="noopener noreferrer"&gt;fluent-ffmpeg&lt;/a&gt; and stream it back to the renderer process:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Finally, simply update your &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;'s &lt;code&gt;src&lt;/code&gt; attribute with the newly created route.&lt;/p&gt;

&lt;p&gt;The only down side is that you need to ship &lt;a href="https://www.ffmpeg.org/download.html" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; with your app. And note that you have to compile it yourself with the &lt;code&gt;--disable-securetransport&lt;/code&gt; flag, otherwise it will be rejected by Apple since it's using the Security API that isn't available while sand-boxed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/47629110/ffmpeg-gets-aborted-in-an-electron-sandboxed-application" rel="noopener noreferrer"&gt;I was stuck at this point for a really long time&lt;/a&gt;, since I couldn't compile a static executable of &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But the issue was that OSX kept dynamic libraries in &lt;code&gt;/usr/local/bin&lt;/code&gt; which all take precedence over everything else. So even if you try to compile your &lt;code&gt;ffmpeg&lt;/code&gt; statically, it won't work with these libraries on the way as they will be linked to your executable.&lt;/p&gt;

&lt;p&gt;So you have to move all those &lt;code&gt;/usr/local/bin/*.dylib&lt;/code&gt; somewhere else, compile the static executable, and TADAAaa... the build will work in the sandbox.&lt;/p&gt;




&lt;p&gt;See? It wasn't all &lt;em&gt;that&lt;/em&gt; bad, you're still here, up and reading. How about we &lt;a href="https://dev.to/yoannmoinet/electron-on-the-app-store-ship-it-4mf0"&gt;Ship it&lt;/a&gt; now?&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>appstore</category>
      <category>menubar</category>
    </item>
    <item>
      <title>Electron on the App Store, Early stages.</title>
      <dc:creator>Yoann Moinet</dc:creator>
      <pubDate>Tue, 09 Feb 2021 21:40:27 +0000</pubDate>
      <link>https://dev.to/yoannmoinet/electron-on-the-app-store-early-stages-27i0</link>
      <guid>https://dev.to/yoannmoinet/electron-on-the-app-store-early-stages-27i0</guid>
      <description>&lt;p&gt;This article is part of a 5 articles series about the publication of an Electron application into the Mac AppStore, &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;Fenêtre&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://fen%C3%AAt.re/" rel="noopener noreferrer"&gt;Fenêtre,&lt;/a&gt; &lt;code&gt;fənɛtʁ&lt;/code&gt;, lets you better multitask on your mac. It enables a &lt;a href="https://en.wikipedia.org/wiki/Picture-in-picture" rel="noopener noreferrer"&gt;picture-in-picture&lt;/a&gt; mode for any website/web-app, image, video or flat file.&lt;br&gt;
You can find the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre-lite/id1288451627" rel="noopener noreferrer"&gt;free version&lt;/a&gt;and the &lt;a href="https://itunes.apple.com/us/app/fen%C3%AAtre/id1286743037" rel="noopener noreferrer"&gt;paid version&lt;/a&gt; on the Mac App-Store.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;My name is &lt;a href="https://yoannmoi.net" rel="noopener noreferrer"&gt;Yoann Moinet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm a French 🇫🇷 Senior Software Engineer at &lt;a href="https://datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; since 2019.&lt;/p&gt;

&lt;p&gt;Writing JavaScript since 2007, I started programming professionally as an ActionScript (Flash ⚡️) developer in 2011 at &lt;a href="http://www.cossette.com" rel="noopener noreferrer"&gt;Cossette&lt;/a&gt; (Montréal based advertising company) and then at &lt;a href="https://www.autodesk.com/" rel="noopener noreferrer"&gt;Autodesk&lt;/a&gt; on &lt;a href="https://www.autodesk.com/products/3ds-max/overview" rel="noopener noreferrer"&gt;3dsMax&lt;/a&gt; in 2013 as a Web Application Engineer, later at &lt;a href="https://www.zendesk.com" rel="noopener noreferrer"&gt;Zendesk&lt;/a&gt; in Montpellier, starting in 2016.&lt;/p&gt;

&lt;p&gt;I wanted to do a post-mortem article on my journey, my stress, my joy and my frustrations conceptualizing, developing, packaging and publishing an Electron application on the Mac AppStore.&lt;/p&gt;

&lt;p&gt;It was a lot of pain and tears, but in the end, I really think it was a positive and teaching experience.&lt;/p&gt;

&lt;p&gt;So much that I ended up writing too much content for just one article and decided to split it into 5 different articles, to ease the pain of reading that much content coming from a Frenchie blabbering in English, about his shitty experience with programming.&lt;/p&gt;




&lt;h1&gt;
  
  
  Inspiration
&lt;/h1&gt;

&lt;p&gt;It all started in early 2017 when a colleague of mine showed me &lt;a href="http://heliumfloats.com/" rel="noopener noreferrer"&gt;Helium&lt;/a&gt; . A very nice piece of software that lets you have a floating window in front of everything else.&lt;/p&gt;

&lt;p&gt;In the following months, I've used it extensively, listening to talks and watching Netflix while coding (&lt;em&gt;please, don't tell my boss&lt;/em&gt; 🙏). But, the more I used it, the more frustrations grew in me. It was missing very niche features I needed. Like a see-through mode, the ability to load local files, a markdown viewer, some code highlighting, more embeds and custom shortcuts...&lt;/p&gt;

&lt;h1&gt;
  
  
  Proof of concept
&lt;/h1&gt;

&lt;p&gt;In the mean time I also wanted to learn more about the Mac application's ecosystem, packaging, submission and review loop.&lt;/p&gt;

&lt;p&gt;So, in June, I started working on a proof of concept to see if I could have a similar feature-set quickly, with a technology I knew well, JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://electron.atom.io/" rel="noopener noreferrer"&gt;Electron&lt;/a&gt; to the rescue (👋 &lt;em&gt;haters&lt;/em&gt;), since all I wanted, primarily, was to load a website in a window, it was &lt;strong&gt;very&lt;/strong&gt; relevant to use a web-view framework.&lt;/p&gt;

&lt;p&gt;A week later, working late hours at home and supporting my very pregnant wife 🤰🏻, I had a first, very rough, PoC, a logo and a minimal feature set. That's all I needed to keep the motivation going for the next 6 months or so (&lt;em&gt;TBD&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/szI6JklWONI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Very first prototype of Fenêtre.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This video helped me hook my colleague, &lt;a href="https://www.producthunt.com/@max_william_neilson" rel="noopener noreferrer"&gt;Max William Neilson&lt;/a&gt;, the biggest Beyonce fan ever, and convinced him to do some designs for Fenêtre. Starting with the logo. Then, the website and the settings screen of the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz4c7wkgce4u8lhgysnsq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz4c7wkgce4u8lhgysnsq.jpeg" alt="1_Ryv3m5_jNWExdv_N7EXMvw" width="800" height="288"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Logo evolution, from my first try on the left to Max's latest, awesomest one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From there I was able to make a nifty little animation in CSS:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/YoannM/embed/MvzxBR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Cost
&lt;/h1&gt;

&lt;p&gt;This was a side project, so I wanted to have something with minimal cost and maintenance possible (&lt;em&gt;because I'm cheap and lazy&lt;/em&gt;). That's why I chose to publish it on the Mac AppStore, following the great advice from another colleague.&lt;/p&gt;

&lt;p&gt;At first, I wanted to handle both licensing and updating myself, but for the small price of 30% of everything, Apple can handle that for you.&lt;/p&gt;

&lt;p&gt;All in all, the application cost me ~115$ (+30% of all sales), which is a fair investment in my opinion... of a cheap person.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apple Developer License 99$&lt;/strong&gt;: this is mandatory if you want to publish your app on the AppStore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain name ~15$&lt;/strong&gt;: I had to buy &lt;a href="https://fen%C3%AAt.re" rel="noopener noreferrer"&gt;fenêt.re&lt;/a&gt; to deliver the website. Some people were having an issue typing the "ê" character, and &lt;strong&gt;MANY&lt;/strong&gt; services still don't support it correctly (&lt;a href="https://dev.to/yoannmoinet/electron-on-the-app-store-pain-tears-nce"&gt;more on this later&lt;/a&gt;). So I finally bought &lt;a href="https://getfenet.re" rel="noopener noreferrer"&gt;getfenet.re&lt;/a&gt; as well, which brings the final cost to 30$, but it's still an exceptional cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private git 0&lt;/strong&gt;$: I'm using &lt;a href="https://about.gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; which offers private repository for free as well as other very neat features, like a CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting 0&lt;/strong&gt;$: Since I wasn't handling licenses or updates I didn't need any infrastructure for these. The only hosting I needed was for the website, which is a simple &lt;a href="https://about.gitlab.com/features/pages/" rel="noopener noreferrer"&gt;GitLab Page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That was all fun and games, wasn't it? Now, come to the dark side and get your hands dirty, come cry with me in the next part of this series, &lt;a href="https://dev.to/yoannmoinet/electron-on-the-app-store-pain-tears-nce"&gt;Pain &amp;amp; tears&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>appstore</category>
      <category>menubar</category>
    </item>
  </channel>
</rss>
