<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.finnpedersenfrance.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.finnpedersenfrance.com/" rel="alternate" type="text/html" /><updated>2026-10-03T18:20:17+02:00</updated><id>https://www.finnpedersenfrance.com/feed.xml</id><title type="html">Finn Pedersen’s Blog</title><subtitle>A blog about technology and development for Microsoft Dynamics 365 Business Central.</subtitle><entry><title type="html">Places to visit in Paris</title><link href="https://www.finnpedersenfrance.com/turism/2026/09/04/Visit-Paris.html" rel="alternate" type="text/html" title="Places to visit in Paris" /><published>2026-09-04T13:00:00+02:00</published><updated>2026-09-07T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/turism/2026/09/04/Visit-Paris</id><content type="html" xml:base="https://www.finnpedersenfrance.com/turism/2026/09/04/Visit-Paris.html"><![CDATA[<h2 id="my-favorite-special-places-to-visit-in-paris">My favorite special places to visit in Paris</h2>

<p>Paris is known by everyone for its beauty and tourism.</p>

<p>I lived in France for 20 years, part of that time in Paris, when I was working for Navision France and following Microsoft France.</p>

<p>In this article, I will share my personal favorite places to visit, with some inside observations and stories you might not have heard before.</p>

<p>Bonus: I will share my three favorite <strong>Champagne 🍾 brands</strong> and the opportunity to flash <strong>Space Invaders 👾</strong> in Paris. Have fun!</p>

<h3 id="places-in-paris">Places in Paris</h3>

<h4 id="grande-arche-de-la-défense">Grande Arche de la Défense</h4>

<p>In 1982, on the initiative of French president François Mitterrand, Danish architect <a href="https://en.wikipedia.org/wiki/Johan_Otto_von_Spreckelsen">Johan Otto von Spreckelsen</a> and Danish engineer <a href="https://da.wikipedia.org/wiki/Erik_Reitzel">Erik Reitzel</a> won the competition with their design of a Grande Arche. Erik Reitzel is especially known for his minimalistic structures, which you can see when visiting the inside of the roof of the building.</p>

<p><img src="/assets/images-01/Grande_Arche_Janvier_2023.jpeg" alt="Grande Arche de la Défense" /></p>

<blockquote>
  <p>Before building this monument, Spreckelsen had only built four churches and his own house.</p>
</blockquote>

<p><a href="https://en.wikipedia.org/wiki/Grande_Arche">Grande Arche de la Défense</a></p>

<p>The building is turned 6.3 degrees due to standing over an underground motorway and train lines.</p>

<h4 id="the-oldest-houses-in-paris">The oldest houses in Paris</h4>

<p>You know Paris for its big avenues like <a href="https://en.wikipedia.org/wiki/Champs-%C3%89lys%C3%A9es">Champs-Elysées</a>.
You should go there. For sure. But how was Paris before <a href="https://en.wikipedia.org/wiki/Haussmann%27s_renovation_of_Paris">Haussmann started renovating Paris</a>?</p>

<p>Three addresses to visit:</p>

<ul>
  <li>13 Rue François Miron, 75004 Paris</li>
  <li>3 Rue Volta, 75003 Paris</li>
  <li>51 Rue de Montmorency, 75003 Paris</li>
</ul>

<h4 id="île-saint-louis">Île Saint-Louis</h4>

<p>Île Saint-Louis is one of my favorite places in Paris. Sitting at a café outside in the sun or finding a restaurant of your taste. Don’t forget to taste the famous <a href="https://berthillon.fr/">Berthillon ice cream</a>.</p>

<p><img src="/assets/images-01/Ile-saint-louis.jpg" alt="Île Saint-Louis" /></p>

<h4 id="le-train-bleu">Le Train Bleu</h4>

<p>One of the most surprising restaurant experiences in Paris is Le Train Bleu, located inside the Gare de Lyon train station.</p>

<p><img src="/assets/images-01/le-train-bleu1.jpg" alt="Le Train Bleu" /></p>

<p><a href="https://www.le-train-bleu.com/en/">Le Train Bleu</a></p>

<p>The Train Bleu restaurant is also known for a scene in <a href="https://en.wikipedia.org/wiki/Luc_Besson">Luc Besson</a>’s 1990 movie <a href="https://en.wikipedia.org/wiki/La_Femme_Nikita_(film)">La Femme Nikita</a>. <a href="https://www.youtube.com/watch?v=4vcT4r7Tljk">Nikita gets her first (test) mission on her birthday</a>.</p>

<blockquote>
  <p>They are drinking Champagne Taittinger millésimé (not on the list below).</p>
</blockquote>

<h4 id="panthéon">Panthéon</h4>

<p>The Panthéon is an impressive monument, but it is actually the inside that I come to visit.</p>

<p>The <strong>Foucault pendulum</strong> is a near-perfect pendulum swinging uniformly relative to the fixed stars. You can thus see the Earth turning.</p>

<p><img src="/assets/images-01/Panthéon_Pendule_de_Foucault.jpg" alt="Foucault pendulum" /></p>

<p>In the crypt of the Panthéon you will find the graves of great men and women honored by France. My favorites are:</p>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/Louis_Braille">Louis Braille</a> enabled blind people to read and write. At the age of 12, he started working on a new system to enable blind children to read and write. After working day and night for three years, at the age of 15 his first version was ready.</li>
  <li><a href="https://en.wikipedia.org/wiki/Marie_Curie">Maria Skłodowska Curie</a> won the Nobel Prize in Physics in 1903 and in Chemistry in 1911. She was the first woman to win the Nobel Prize and the first person to win it twice. She was the first woman to become a professor at the University of Paris. The first woman to enter the Panthéon for her own merits.</li>
  <li><a href="https://en.wikipedia.org/wiki/Joseph-Louis_Lagrange">Joseph-Louis Lagrange</a>. Maybe you heard that the <a href="https://en.wikipedia.org/wiki/James_Webb_Space_Telescope">James Webb Space Telescope</a> and the <a href="https://en.wikipedia.org/wiki/Nancy_Grace_Roman_Space_Telescope">Nancy Grace Roman Space Telescope</a> were sent to <a href="https://en.wikipedia.org/wiki/Lagrange_point">Lagrange point 2</a>. Well, he figured it out in 1772.</li>
  <li><a href="https://fr.wikipedia.org/wiki/Simone_Veil">Simone Veil</a> survived Auschwitz, became a leading defender of women’s rights, and as Minister of Health secured the legalization of abortion in France in 1975.</li>
</ul>

<p><a href="https://www.paris-pantheon.fr/">Panthéon</a></p>

<h4 id="observatoire-de-paris">Observatoire de Paris</h4>

<p>The <a href="https://en.wikipedia.org/wiki/Paris_Observatory">Paris Observatory</a> is actually not easy to visit. You will find it near the Jardin du Luxembourg.
The reason I mention it is to tell a story.</p>

<blockquote>
  <p>A story of giants standing on the shoulders of giants.</p>
</blockquote>

<p><a href="https://en.wikipedia.org/wiki/Nicolaus_Copernicus">Nicolaus Copernicus</a> (1473-1543) published <a href="https://en.wikipedia.org/wiki/De_revolutionibus_orbium_coelestium">De revolutionibus orbium coelestium</a> just before his death.
The Danish astronomer <a href="https://en.wikipedia.org/wiki/Tycho_Brahe">Tycho Brahe</a> (1546-1601) made precise observations of the planet Mars. His student <a href="https://en.wikipedia.org/wiki/Johannes_Kepler">Johannes Kepler</a> (1574-1630) used Tycho’s records of the motion of Mars to deduce the laws of planetary motion. In January 1610, <a href="https://en.wikipedia.org/wiki/Galileo_Galilei">Galileo Galilei</a> (1564-1642) observed Jupiter and its four largest moons with a telescope he built himself. He named them Io, Europa, Ganymede, and Callisto — now called the Galilean moons.
In 1671 the <strong>Paris Observatory</strong> sent the French astronomer <a href="https://en.wikipedia.org/wiki/Jean_Picard">Jean Picard</a> (1620-1682) to Denmark to determine the exact position of Tycho Brahe’s former observatory at Uraniborg on Hven. While in Denmark, Picard met <a href="https://en.wikipedia.org/wiki/Ole_R%C3%B8mer">Ole Rømer</a> (1644-1710), recognized his talent, and brought him back to Paris in 1672, where Rømer joined the Paris Observatory. Here he observed eclipses of Jupiter’s moon Io.
In 1676, Rømer established that light has a finite speed. In 1862, <a href="https://en.wikipedia.org/wiki/L%C3%A9on_Foucault">Léon Foucault</a> (1819-1868) (<em>see the Panthéon just above</em>) measured the speed of light by reflecting a beam from a rapidly rotating mirror, obtaining about 298,000 km/s—remarkably close to the modern value.</p>

<p>In 1905, <a href="https://en.wikipedia.org/wiki/Albert_Einstein">Albert Einstein</a> concluded that the speed of light is invariant, so space and time must be relative.</p>

<h3 id="more-places-in-paris">More places in Paris</h3>

<p>My daughter is currently studying in Paris and asked me to add some of her favorite places to the list.
I completely support her suggestions. These are all places I love to visit myself.</p>

<h4 id="la-crêperie-beaubourg">La crêperie Beaubourg</h4>

<p><img src="/assets/images-01/creperiebeaubourg.png" alt="La crêperie Beaubourg" /></p>

<p><a href="https://creperiebeaubourg.com/">La crêperie Beaubourg</a> is right next to the Centre Pompidou, overlooking the colorful Niki de Saint Phalle and Tinguely fountains. It is a great place for traditional Breton galettes and crêpes, with good cider, whisky, Mariage Frères tea and homemade chocolate. The atmosphere is relaxed, and sitting outside by the fountain is especially nice.</p>

<h4 id="basilique-du-sacré-cœur-de-montmartre">Basilique du Sacré-Cœur de Montmartre</h4>

<p><img src="/assets/images-01/Sacré-Cœur-de-Montmartre.jpg" alt="Basilique du Sacré-Cœur de Montmartre" /></p>

<p><a href="https://en.wikipedia.org/wiki/Sacr%C3%A9-C%C5%93ur,_Paris">Sacré-Cœur</a> is one of the best places to look out over Paris.</p>

<p>During the night of 20–21 April 1944, thirteen bombs fell extremely close to the Sacré-Cœur. Despite the explosions striking the basilica and nearby houses, nobody was killed. Inside the basilica, you will find a plaque recalling the event, showing where the bombs fell.</p>

<blockquote>
  <p>Some think it was divine intervention; others think it was pure luck.</p>
</blockquote>

<p>The fact is that had the bombs been just a few meters to one side, the basilica could have been completely destroyed.</p>

<p><img src="/assets/images-01/Bombs_Basilique_Sacré_Cœur_Montmartre.jpg" alt="Thirteen bombs fell extremely close to the Sacré-Cœur" /></p>

<h4 id="petit-palais">Petit Palais</h4>

<p>The <a href="https://www.petitpalais.paris.fr/">Petit Palais</a> is an art museum today. It is located just in front of the Grand Palais, which you might have seen transformed into a sports arena during the Olympic Games in 2024. Both buildings are iconic and are located close to the Champs-Elysées.</p>

<h3 id="sculptures-in-paris">Sculptures in Paris</h3>

<p>I love sculptures. Here are my four favorite sculptures in Paris.</p>

<h4 id="lion-of-belfort-at-place-denfert-rochereau">Lion of Belfort at Place Denfert-Rochereau</h4>

<p>The lion symbolizes the heroic French resistance during the Siege of Belfort, a 103-day Prussian assault from December 1870 to February 1871.</p>

<p><img src="/assets/images-01/Lionbelfort.jpg" alt="Lion of Belfort at Place Denfert-Rochereau" /></p>

<p><a href="https://en.wikipedia.org/wiki/Lion_of_Belfort">Lion de Belfort on Wikipedia</a></p>

<h4 id="ours-blanc-by-françois-pompon-at-musée-dorsay">Ours blanc by François Pompon at Musée d’Orsay</h4>

<p>François Pompon worked as an assistant for Auguste Rodin and Camille Claudel.
At the age of 67 he made the polar bear.</p>

<blockquote>
  <p>An entire life leading up to this moment.</p>
</blockquote>

<p><img src="/assets/images-01/pompon_loursblanc.jpg" alt="Ours blanc by François Pompon" /></p>

<p><a href="https://www.musee-orsay.fr/en/artworks/ours-blanc-15496">Ours blanc at Musée d’Orsay</a></p>

<h4 id="sculptures-at-the-louvre">Sculptures at the Louvre</h4>

<p>The Louvre has a fantastic collection of sculptures. My favorites are the slaves by Michelangelo.</p>

<p><img src="/assets/images-01/Dying_Slave_by_Michelangelo_Musée_de_Louvre.jpg" alt="Dying Slave by Michelangelo" /></p>

<p><a href="https://www.louvre.fr/en/explore/visitor-trails/the-louvre-s-masterpieces/imprisoned-in-stone">Imprisoned in stone, The Louvre</a></p>

<h4 id="the-thinker-by-rodin">The Thinker by Rodin</h4>

<p>The Thinker was originally entitled The Poet, representing Dante, the author of the Divine Comedy.</p>

<p>This powerful man, lost in thought, maybe observing the world.</p>

<blockquote>
  <p>If he could, he would be shaking his head.</p>
</blockquote>

<p><img src="/assets/images-01/TheThinkerRodin.jpg" alt="The Thinker by Rodin" /></p>

<p><a href="https://www.musee-rodin.fr/en/musee/collections/oeuvres/penseur-meudon">The Thinker, Musée Rodin</a></p>

<h3 id="my-favorite-champagne-brands">My favorite Champagne brands</h3>

<p>I lived in a Champagne village for many years. The list is ordered by how much I drink of it.</p>

<h4 id="champagne-henri-chauvet-in-rilly-la-montagne">Champagne Henri Chauvet in Rilly-la-Montagne</h4>

<p>The Chauvet family has complete control over the entire process.
The wine fields, when to pick the grapes, the pressing, maturing the wine, bottling, and selling.</p>

<p>I always have a bottle of Chauvet in my fridge. Rosé, Brut Nature (dry) or Réserve. Because, you never know when there might be something to celebrate.</p>

<p><a href="https://champagne-chauvet.com/">Champagne Henri Chauvet</a></p>

<p>If you want a great champagne at a fair price, go with Henri Chauvet. Do like me, buy two boxes with a mix of your favorite bottles and keep one in the fridge at all times.</p>

<h4 id="champagne-vilmart-in-rilly-la-montagne">Champagne Vilmart in Rilly-la-Montagne</h4>

<p>Laurent Champs leaves nothing to chance. The wine is aged in barrels. I have seen an article where Vilmart beats the next on the list. It is not cheap, but you get what you pay for.</p>

<p><a href="https://www.champagnevilmart.fr/en/">Champagne Vilmart et Cie</a></p>

<p>If you want an extraordinary champagne at a fair price, go with Vilmart.</p>

<h4 id="champagne-bollinger-in-aÿ">Champagne Bollinger in Aÿ</h4>

<p>Of course, you think James Bond, Agent 007, but Champagne Bollinger is actually an incredible Champagne.
Bollinger makes no compromises. The quality and taste are simply extraordinary. So is the price.</p>

<p><a href="https://www.champagne-bollinger.com/en/">Champagne Bollinger</a></p>

<p>If you want the best no matter the price, go with Bollinger.</p>

<p>Fun fact: Bollinger uses Business Central and was once my customer.</p>

<h3 id="places-in-paris-that-didnt-make-the-list">Places in Paris that didn’t make the list</h3>

<p>My list of places in Paris is actually a bit longer, but I had to stop somewhere.</p>

<p>Places in Paris that didn’t make the list, but are still worth mentioning:</p>

<ul>
  <li>Place des Vosges</li>
  <li>Maison de Victor Hugo, Place des Vosges</li>
  <li><a href="https://www.operadeparis.fr/">Opéra Bastille</a> offers an incredible opera experience</li>
  <li>The entrance to the underground canal, Place de la Bastille</li>
  <li><a href="https://www.cieldeparis.com/">Tour Montparnasse</a>. Visit the roof and get the best view over Paris</li>
  <li>Trocadéro. Best view of the Eiffel Tower</li>
  <li>Musée de l’Homme and <a href="https://www.musee-marine.fr/">Musée de la Marine</a>. Trocadéro</li>
  <li>Les Halles. Métro and train in all directions. Bars and restaurants nearby.</li>
  <li><a href="https://www.ihp.fr/fr/musee-maison-poincare">Institut Henri Poincaré</a></li>
  <li><a href="https://www.la-mosquee.com/">Restaurant at Mosquée de Paris</a>, next to Jardin des Plantes</li>
  <li><a href="https://www.mnhn.fr/fr">Muséum national d’Histoire naturelle</a>, in Jardin des Plantes</li>
  <li>Jardin des Plantes</li>
  <li>Louis Vuitton Foundation</li>
  <li>Jardin du Luxembourg</li>
  <li>Institut du monde arabe</li>
  <li>Rue Mouffetard 5e</li>
  <li>Sacré-Cœur</li>
  <li>Cathédrale Notre-Dame de Paris</li>
  <li>La Coupole</li>
</ul>

<p>There is much more, but I think you should go and find them yourself. Enjoy.</p>

<h4 id="hunting-for-space-invaders-in-paris-">Hunting for Space Invaders in Paris 👾</h4>

<p>When you are out walking in Paris, why not have some fun? Watch out for the Space Invaders and see if you can catch some of them.</p>

<p><a href="https://www.space-invaders.com/flashinvaders/">Get the FlashInvaders app</a></p>

<p><a href="https://www.space-invaders.com/world/paris/">Hunting for Space Invaders in Paris</a></p>]]></content><author><name>Finn</name></author><category term="Turism" /><summary type="html"><![CDATA[My favorite special places to visit in Paris]]></summary></entry><entry><title type="html">Modern Business Central Development Is Everyone’s Business</title><link href="https://www.finnpedersenfrance.com/programming/2026/09/04/modern-bc-development.html" rel="alternate" type="text/html" title="Modern Business Central Development Is Everyone’s Business" /><published>2026-09-04T07:00:00+02:00</published><updated>2026-09-10T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2026/09/04/modern-bc-development</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2026/09/04/modern-bc-development.html"><![CDATA[<!-- markdownlint-disable MD024 -->
<h2 id="modern-business-central-development-is-everyones-business">Modern Business Central Development Is Everyone’s Business</h2>

<blockquote>
  <p>You are the CEO, CFO, or Head of IT in a company that uses <strong>Microsoft Dynamics 365 Business Central</strong>. This article is written for non-developers.</p>

  <p>If you are a developer, you should read it too and talk with your manager about it.</p>
</blockquote>

<p>In this article, I will describe a number of important aspects of modern software development that you need to understand and ensure are addressed in your Business Central solution.</p>

<blockquote>
  <p>My inspiration comes from auditing customer solutions, interviews with hiring managers of dev teams, and discussions with peer developers.</p>
</blockquote>

<h3 id="you-asked-for-bad-code">You asked for bad code</h3>

<blockquote>
  <p>The customer or the manager asked for bad spaghetti code, with no tests, hard to maintain and understand. Then they asked the developer to ignore every rule, convention, and standard. Instead, they insisted that they invented their own patterns.
They further asked that the development be done by people with no software education whatsoever, as long as they called themselves developers or sometimes senior developers.</p>
</blockquote>

<p><strong>Of course not</strong>, so how did we end up here? Precisely because the customer or the manager did not explicitly ask for quality, modernization, tests, or to follow best practices. HR hired these developers based on years of experience, with little interest in diplomas.</p>

<p>Was price always the only topic discussed? Apparently. Robustness was definitely never in the Statement of Work.</p>

<blockquote>
  <p><strong>This is what is about to change</strong></p>
</blockquote>

<h2 id="opinionated">Opinionated</h2>

<p>So what should you ask for? What should you have a strong opinion about? And why?</p>

<p>Take the following list to your Microsoft partner and simply ask:</p>

<blockquote>
  <p><strong>How are we doing compared to this list?</strong></p>
</blockquote>

<h3 id="update-business-central-every-month">Update Business Central every month</h3>

<p>Let us start here. This might be the single most important difference between Business Central in the Cloud and any on-premises solution.</p>

<h4 id="why">Why?</h4>

<p>You are paying for a solution, for the maintenance of this solution, for new features in this solution, and you want a return on this investment!</p>

<p>And what you don’t know is the risk you were taking.
One of the most dangerous situations in the past, when you were not continuously upgrading, was that you ended up with a unique combination of versions. You were on a specific version of Dynamics NAV, some service pack or hotfix version of Continia, some random version of an industry-specific solution, and so on — basically a unique combination of apps never tested together.</p>

<blockquote>
  <p>With Business Central in the Cloud, everyone is on the same or very few versions. This reduces the risk of unknown combinations.</p>
</blockquote>

<h4 id="additional-reading">Additional Reading</h4>

<ul>
  <li><a href="/programming/2024/03/21/maintenance-mode.html">Customizations in Maintenance Mode</a></li>
  <li><a href="/programming/2024/03/21/breaking-changes-bc26.html">Breaking Changes in Version 26</a></li>
</ul>

<blockquote>
  <p><strong>Don’t</strong> be tempted to skip minor versions and only update every six months or once a year.</p>
</blockquote>

<p><img src="/assets/images-01/New-upgrade-strategy.png" alt="New Upgrade Strategy" /></p>

<h3 id="automated-tests-of-business-critical-logic">Automated Tests of Business-Critical Logic</h3>

<p>This leads us to automated tests.
Tests are simply an essential and integral part of any modern development framework.</p>

<h4 id="next-level-testing">Next Level Testing</h4>

<p>Ask for:</p>

<ul>
  <li>Performance Testing</li>
  <li>Page scripting tests</li>
  <li>100% code coverage of core functionality</li>
</ul>

<p><img src="/assets/images-01/100pctCodeCoverage.png" alt="100 pct Code Coverage" /></p>

<h4 id="why-1">Why?</h4>

<p>You cannot ask the entire company to test the entire solution every last Friday of the month. This is not going to happen.
It wouldn’t give you any guarantee of quality anyway.
Quality is not something you accidentally add at the end of a process.
This is just as true for Business Central development as for any other process.</p>

<h4 id="additional-reading-1">Additional Reading</h4>

<ul>
  <li><a href="/programming/2024/01/03/Automated-Tests-in-AL.html">Automated Tests in AL</a></li>
  <li><a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a></li>
  <li><a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces – Part 1</a></li>
  <li><a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
  <li><a href="/programming/2024/03/21/breaking-changes-bc26.html">Breaking Changes in Version 26</a></li>
</ul>

<blockquote>
  <p><strong>Don’t</strong> be tempted to skip this part to save money or time.</p>
</blockquote>

<h3 id="modern-code-architecture">Modern Code Architecture</h3>

<p>Here is what to ask for:</p>

<ul>
  <li>Independent logic should be organized in separate Apps</li>
  <li>Within apps, features are organized in separate folders</li>
  <li>Business logic by feature is organized in Codeunits</li>
  <li>Cleanup and remove logic that is no longer used</li>
</ul>

<h4 id="why-2">Why?</h4>

<p>Well-organized code is a prerequisite for keeping maintenance and test writing affordable.
It will save you money over time.</p>

<ul>
  <li>Now the developer can focus on testing the logic in these Codeunits.</li>
  <li>This will allow you to implement Namespaces.</li>
</ul>

<p>Modular code is easier to read, understand, update, and maintain.
It will be easier to identify and remove parts of the code that are no longer used.</p>

<p>This is also how Microsoft is organizing the Business Central code. It is simply best practice.</p>

<h4 id="additional-reading-2">Additional Reading</h4>

<ul>
  <li><a href="/programming/2023/12/28/Namespaces-in-AL.html">Namespaces in AL</a></li>
  <li><a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces – Part 1</a></li>
  <li><a href="/programming/2024/09/08/BC-RecordLinks.html">Record Links and Anti-patterns</a></li>
  <li><a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
</ul>

<blockquote>
  <p><strong>Don’t</strong> be tempted to keep the old organization by Object Type.</p>
</blockquote>

<p><img src="/assets/images-01/CleanRoomReverseEngineering.png" alt="Clean Room Reverse Engineering" /></p>

<h3 id="get-your-code-on-your-github">Get your code on your GitHub</h3>

<p>Take ownership of your own code. This is fundamental.</p>

<h4 id="why-3">Why?</h4>

<ul>
  <li>The code in a custom-made extension or app is the property of the customer. So it is your code.</li>
  <li>The build pipeline ensures that all the tests are run each time something changes.</li>
  <li>Control who has access to your code.</li>
  <li>New requirements can be described and managed using GitHub Issues.</li>
  <li>Know who changed what and when. Who approved the Pull Request?</li>
  <li>Actively use AI to review changes, understand new requests, and even develop simple changes.</li>
  <li>Use AL-Go for GitHub.</li>
  <li>Enable and facilitate collaboration around the solution.</li>
</ul>

<blockquote>
  <p><strong>Don’t</strong> just use GitHub to store the code.</p>
</blockquote>

<h4 id="additional-reading-3">Additional Reading</h4>

<ul>
  <li><a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
</ul>

<h3 id="code-analyzers-and-naming-conventions">Code Analyzers and Naming Conventions</h3>

<ul>
  <li>Activate code analyzers and ensure settings are included in the project.</li>
  <li>Enable Automated cleanup on save or on a regular basis.</li>
  <li>Naming Conventions for Fields, Tables, and other Objects must be followed.</li>
  <li>Ask to see the <code class="language-plaintext highlighter-rouge">settings.json</code> file in the project.</li>
</ul>

<p>The standard code analyzers are:</p>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop">Code Cop</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/uicop">UI Cop</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/pertenantextensioncop">Per Tenant Extension Cop</a></li>
</ul>

<h4 id="next-level-code-analyzers">Next Level Code Analyzers</h4>

<p><a href="https://alcops.dev/docs/">AL Cops</a> is adding six new analyzers to the standard three. These are, in my opinion, an absolute must-have.</p>

<h4 id="why-4">Why?</h4>

<p>Business Central development and GitHub enable collaboration. This is great.
Multiple developers can work on the same project. If one is on vacation, another can jump in.
With good code organization and tests, we are in a good place. But …</p>

<p>We want the developers to follow the same conventions, the same coding guidelines, the same rules.
This is as true today as it was in 1994 when the DOS version of Navision was still around.</p>

<blockquote>
  <p><strong>The rule</strong>: When looking at the code, you should not be able to tell who wrote it.</p>
</blockquote>

<p><em>This is of course completely fundamental for allowing AI to do some of the coding.</em></p>

<h4 id="additional-reading-4">Additional Reading</h4>

<ul>
  <li><a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
  <li><a href="/programming/2023/10/10/BC-Rulesets.html">Rulesets in Business Central</a></li>
  <li><a href="/programming/2026/07/22/there-is-a-new-sheriff-in-town.html">There is a new sheriff in town</a></li>
  <li><a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced CodeCop Analyzer and Custom Rulesets</a></li>
</ul>

<p><img src="/assets/images-01/NewSheriffInTownV2.png" alt="Robot Sheriff and Dinosaur in jail" /></p>

<h3 id="usability">Usability</h3>

<p>With Business Central, we can make the customer solution much more user-friendly.</p>

<p>Ask for usability tests. Enable user feedback and keep a budget for improving usability.</p>

<p>This means asking that these properties be filled in with useful information.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">AboutTitle</code> and <code class="language-plaintext highlighter-rouge">AboutText</code> are used for onboarding. (NEW)</li>
  <li><code class="language-plaintext highlighter-rouge">HelpLink</code> on Pages points to the relevant help page on your website.</li>
  <li><code class="language-plaintext highlighter-rouge">Tooltips</code> on Tables and Pages give the user additional information on the page.</li>
  <li>All development must be in English, and thus Translations are necessary.</li>
</ul>

<blockquote>
  <p>Your solution should have its own website with documentation.
Ask that it is written in <code class="language-plaintext highlighter-rouge">Markdown</code> format and is part of the project.</p>
</blockquote>

<h4 id="next-level-usability">Next Level Usability</h4>

<ul>
  <li>Ask that the solution has <strong>integrated support, event logging, and telemetry</strong>.</li>
  <li>Extend the documentation with a demo script and information on the architecture. (Use <a href="https://mermaid.ai/open-source/">Mermaid</a>).</li>
</ul>

<h4 id="why-5">Why?</h4>

<p>Ask your users why good usability is good business. They will tell you.</p>

<h3 id="security-evaluation">Security Evaluation</h3>

<p>Keep your employees, your solution, and your company safe.</p>

<ul>
  <li>Make sure the users, and in the future AI, have the right permissions to view and change data.</li>
  <li>Make sure that only qualified developers have access to your code.</li>
  <li>Make sure that the code is safe.</li>
</ul>

<h3 id="transparency-around-ai-in-development">Transparency around AI in Development</h3>

<p>AI will have a role in developing and maintaining your solution. Ask for transparency in how it is used.</p>

<h4 id="why-6">Why?</h4>

<p>AI will play a bigger and bigger role in software development, and you have the right to transparency about what exactly you are getting for your money.</p>

<h3 id="modernization">Modernization</h3>

<p>Ask that your solution is being modernized.</p>

<h4 id="why-7">Why?</h4>

<p>Microsoft is continuously introducing new development concepts like <strong>Namespaces</strong>, <strong>Interfaces</strong>, and <strong>Dictionaries</strong>.</p>

<p>New <strong>RuleSets</strong> are being added, enabling developers to find performance issues and potential runtime errors before they happen.</p>

<p>Making these improvements and adopting these new concepts on an ongoing basis will keep your solution modern and up to date.</p>

<h4 id="additional-reading-5">Additional Reading</h4>

<ul>
  <li><a href="/programming/2025/11/30/urgent-need-to-modernize-bc-development.html">The urgent need to modernize Business Central development</a></li>
</ul>

<p><img src="/assets/images-01/DinosaurAIandBCnew.png" alt="Dinosaur AI and Business Central" /></p>

<h3 id="evaluate-technical-debt">Evaluate Technical Debt</h3>

<p>Ask for an evaluation of technical debt, especially if you upgraded your solution from Dynamics NAV or BC on-premises.
The developer working in the old Object Designer did not have all the help we have today.
This means that code converted from C/AL to AL is not always optimal.</p>

<p>Try to find out how far back the oldest changes in your BC solution go. For some organizations, this might date back 30 years.</p>

<h4 id="why-8">Why?</h4>

<p>Business Central in the Cloud lives in a dynamic environment.
Technical debt is building up, and it will slow you down and cost you money.</p>

<h4 id="additional-reading-6">Additional Reading</h4>

<ul>
  <li><a href="/programming/2024/03/21/breaking-changes-bc26.html">Now is a good time to clean up</a></li>
</ul>

<h3 id="ask-for-software-educated-developers">Ask for software-educated developers</h3>

<ul>
  <li>Ask who will be working on your solution.</li>
  <li>Ask for their CV and proof of relevant software education.</li>
  <li>Do not assume that years of Dynamics NAV experience is enough.</li>
  <li>Ask that the developers go to conferences and actively follow training sessions.</li>
</ul>

<h4 id="why-9">Why?</h4>

<p>Many self-taught NAV developers can still customize BC, but have never worked with modern concepts. From my audits, I typically see a lack of understanding of the difference between something that works and something that is robust, testable, maintainable, customizable, and easy to support.</p>

<p>This requirement is aligned with everything mentioned above.</p>

<blockquote>
  <p>Developing Business Central extensions requires modern software development skills.</p>
</blockquote>

<h2 id="one-coherent-vision-of-modern-software-development">One coherent vision of modern software development</h2>

<p>These opinions are not a menu you can pick from. They form one coherent vision of modern software development for Business Central. You can safely apply monthly updates because your code is tested. Writing tests is affordable because your code is well-organized. Organization only works if your team follows the same rules, owns the code, and keeps modernizing. Usability, security, AI transparency, and qualified developers belong to the same picture. Skip one part, and the rest will not hold. You need all of it.</p>

<blockquote>
  <p>Getting it right from the start costs almost nothing extra.</p>
</blockquote>

<h2 id="contact-me">Contact me</h2>

<p>Reach out if you want to have an informal talk about an audit of your solution.</p>

<p>Finn Pedersen</p>

<p>Modern BC Developer</p>

<h2 id="directions-emea-2026-in-paris">Directions EMEA 2026 in Paris</h2>

<p>I have been invited to talk about this topic at Directions EMEA in Paris, France.
The conference is from October 27th-29th 2026.</p>

<p><a href="https://sessionize.com/s/finn-pedersen/opinionated-software-development-for-customers-and/184210">Modern Business Central Development Is Everyone’s Business</a></p>

<p><img src="/assets/images-01/modern-business-central-development-emea-2026.jpeg" alt="Directions EMEA 2026 in Paris" /></p>

<p>Reach out to me if you are interested in a <a href="https://sessionize.com/finn-pedersen">specific training</a> for your company.</p>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[Modern Business Central Development Is Everyone’s Business]]></summary></entry><entry><title type="html">France says no to Tesla FSD (Politics)</title><link href="https://www.finnpedersenfrance.com/politics/2026/07/24/FSD-not-approved-in-france.html" rel="alternate" type="text/html" title="France says no to Tesla FSD (Politics)" /><published>2026-07-24T07:00:00+02:00</published><updated>2026-09-07T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/politics/2026/07/24/FSD-not-approved-in-france</id><content type="html" xml:base="https://www.finnpedersenfrance.com/politics/2026/07/24/FSD-not-approved-in-france.html"><![CDATA[<h2 id="france-says-no-to-tesla-fsd-for-now">France says no to Tesla FSD (for now)</h2>

<h3 id="update-september-2nd-2026">Update September 2nd, 2026</h3>

<p><img src="/assets/images-01/Tabarot-Musk1.jpg" alt="Philippe Tabarot has a virtual meeting with Elon Musk" /></p>

<blockquote>
  <p><strong>BREAKING NEWS</strong>: Philippe Tabarot held a virtual meeting with Elon Musk.</p>
</blockquote>

<p><a href="https://x.com/PhilippeTabarot/status/2095225468035170725?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E2095225468035170725%7Ctwgr%5Eaa02f039be5d14bbeeb8d1f44d3ba7866a6c20be%7Ctwcon%5Es1_c10&amp;ref_url=https%3A%2F%2Fpublish.x.com%2F%3Furl%3Dhttps%3A%2F%2Ftwitter.com%2FPhilippeTabarot%2Fstatus%2F2095225468035170725">French Transport Minister Philippe Tabarot shared details on X of a virtual meeting with Elon Musk focused on Tesla’s Full Self-Driving (FSD) system.</a></p>

<p>French authorities have worked with Tesla for months on technical changes needed for vehicle approval. European-level homologation is expected soon, and road tests are now starting with two FSD-equipped vehicles.
The post highlights ongoing European efforts to integrate advanced autonomous driving tech, underscored by Musk’s ❤️🇫🇷 reply.</p>

<h3 id="original-article">Original Article</h3>

<p><img src="/assets/images-01/PhilippeTabarot.jpg" alt="Philippe Tabarot" /></p>

<p>On July 22, 2026, the French Minister of Transport, <a href="https://www.info.gouv.fr/personnalite/philippe-tabarot"><strong>Philippe Tabarot</strong></a>, answered a citizen’s question on the French government’s participation platform <a href="https://www.agora.gouv.fr/qags/918b7863-c9e7-4dfb-9a2e-b54feb7e1629">Agora</a>. The answer, in short: <strong>No. Tesla FSD (Supervised) will not be authorized in France. Not yet.</strong></p>

<p>You can watch the video with the minister’s answer on the <a href="https://www.agora.gouv.fr/qags/918b7863-c9e7-4dfb-9a2e-b54feb7e1629">Agora platform</a>, and the decision was covered widely in the French press, for example by <a href="https://www.lefigaro.fr/secteur/high-tech/la-france-refuse-pour-le-moment-d-autoriser-le-systeme-de-conduite-autonome-supervisee-de-tesla-20260723">Le Figaro</a> and <a href="https://www.numerama.com/vroom/2299755-coup-dur-pour-tesla-le-fsd-ne-sera-pas-autorise-en-france.html">Numerama</a>. The Danish motoring organization FDM writes <a href="https://fdm.dk/nyheder/nyt-om-trafik-og-biler/tesla-ejere-i-danmark-faar-lov-at-slippe-rattet">Tesla-ejere i Danmark får lov at slippe rattet</a>, and the official website of Færdselsstyrelsen announces <a href="https://www.fstyr.dk/nyheder/2026/jun/teslas-foererassistentsystem-fsd-supervised-er-foreloebigt-godkendt-i-danmark">Teslas førerassistentsystem FSD Supervised er foreløbigt godkendt i Danmark</a>.</p>

<p>I live in Denmark, and here FSD (Supervised) was <a href="https://electrek.co/2026/06/09/tesla-fsd-approved-denmark-fourth-european-country/">provisionally approved on June 9, 2026</a> by the Danish Road Traffic Authority, Færdselsstyrelsen. The Netherlands was <a href="https://www.rdw.nl/en/news/2026/rdw-explanation-of-european-type-approval-tesla-with-provisional-validity-in-the-netherlands">first, on April 10, 2026</a>, when the Dutch vehicle authority RDW granted a European type approval with provisional validity limited to the Netherlands, after more than one and a half years of testing. Lithuania, Estonia, and Belgium followed.</p>

<p>The minister’s answer reveals a lot, and that is what this article investigates.</p>

<h2 id="the-question-from-ludovic">The question from Ludovic</h2>

<blockquote>
  <p>Supervised autonomous driving, approved in the Netherlands, could help save lives by limiting human error. Is France considering authorizing it soon?</p>
</blockquote>

<p>The full transcript of the minister’s answer, translated into English, is at the end of this article. Read it. It is worth your time.</p>

<h2 id="intro-i-bought-fsd-in-2019">Intro: I bought FSD in 2019</h2>

<p>In 2019, I bought a Tesla Model 3 and added the FSD option. <strong>I have been waiting for seven years!</strong></p>

<h3 id="what-does-it-mean-to-me">What does it mean to me?</h3>

<h4 id="driving-from-denmark-to-france">Driving from Denmark to France</h4>

<p>I never thought of letting the car drive in the city. My interest was letting it drive the 1.200 km on the highway from Denmark to France. That would have been really useful.</p>

<p>If FSD cannot cross the borders into Sweden, Germany, the Netherlands, Belgium, and France, then it is just a gadget.</p>

<h4 id="people-who-are-blind-or-have-low-vision">People who are blind or have low vision</h4>

<p>My son was slowly losing his vision at that time, and I told him that by the time he turned 18 in 2026, they would have solved the challenge and that one day, he would be able to own a car.
Little did I expect that public administration would get in the way of the technology.</p>

<h4 id="safety">Safety</h4>

<p>In 1987, my mother was killed in her car when a truck driver lost control of his vehicle.
To me, it is personal, and I have no problem with all trucks becoming autonomous.</p>

<h4 id="electric-car">Electric Car</h4>

<p>I have driven almost 200.000 km in my Tesla Model 3. Assuming a comparable gasoline car consuming 5 liters per 100 km, that corresponds to roughly 10.000 liters of gasoline that would have had to be pumped out of the ground, refined, and transported to a gas station. Burning that gasoline would have produced about 23 metric tons of CO2 in tailpipe emissions alone. The comparison covers tailpipe emissions only and does not include the emissions from producing the electricity I charged with.</p>

<h2 id="everyone-else-is-late-to-the-party">Everyone else is late to the party</h2>

<h3 id="tesla">Tesla</h3>

<p>Tesla has been talking about self-driving cars since 2016.
All of their models, including the cheapest Model 3, have been equipped with cameras and an onboard computer intended for self-driving since 2017, though the hardware has been upgraded several times since then.</p>

<p>On the
<a href="https://www.youtube.com/watch?v=j0z4FweCy4M">Tesla AI Day 2021</a>,
they shared progress, data, problems, and information.</p>

<p>Tesla does not publish a cumulative figure for its FSD investment, but my estimate is that the company has spent more than $14 billion on AI infrastructure, research, and development over the past 10 years. Not all of that is FSD-specific, but the order of magnitude is clear.</p>

<h3 id="the-european-union">The European Union</h3>

<p>Now that Tesla is actually rolling out FSD Supervised on public roads, Europe is reacting, or should I say <em>panicking</em>.</p>

<p>The good news is that the EU understands that it has to harmonize the rules for all countries.
No European car maker will ever succeed in making autonomous vehicles for all the different countries in the world. Especially the small ones. This should also be good news for human drivers and other car makers.</p>

<h4 id="2025-03-05-industrial-action-plan-for-the-european-automotive-sector">2025-03-05 Industrial Action Plan for the European automotive sector</h4>

<p>Get the
<a href="https://transport.ec.europa.eu/document/download/89b3143e-09b6-4ae6-a826-932b90ed0816_en?filename=Communication%20-%20Action%20Plan.pdf">PDF</a>
here.</p>

<h4 id="2026-06-08-directorate-general-for-mobility-and-transport">2026-06-08 Directorate-General for Mobility and Transport</h4>

<p>18 Member States signed a Joint Declaration of Intent on large-scale cross-border testbeds for autonomous vehicles.</p>

<p><a href="https://transport.ec.europa.eu/news-events/news/eu-ministers-back-cross-border-initiative-autonomous-vehicle-testbeds-2026-06-08_en">EU Ministers back cross-border initiative for autonomous vehicle testbeds</a></p>

<p>Note that as a first supporting measure, the Connecting Europe Facility (CEF) 2026 work programme foresees 20 million euros for digital infrastructure dedicated to autonomous driving under the reflow call to be launched in June this year.</p>

<p>Twenty million euros will not get them far. Tesla, a single company, has had to spend billions of dollars, and even Tesla is not there yet.</p>

<h2 id="why-are-we-interested-in-autonomous-vehicles">Why are we interested in autonomous vehicles?</h2>

<h3 id="autonomous-vehicles-can-save-lives">Autonomous vehicles can save lives</h3>

<p>That is the promise, according to <a href="https://x.com/elonmusk/status/2079965837382037520">Elon Musk on X</a>, and it is the reason Ludovic asked his question in the first place.</p>

<h3 id="some-people-cannot-or-prefer-not-to-drive-a-car">Some people cannot or prefer not to drive a car</h3>

<ul>
  <li>People with low vision or other disabilities</li>
  <li>People who have a driver’s license but are not comfortable driving</li>
  <li>People with no driver’s license</li>
</ul>

<h2 id="protection-of-the-european-car-industry">Protection of the European car industry</h2>

<p>This looks like protectionism to me. France, Germany, and the other car-making countries in Europe have strong industrial reasons to delay Tesla, and I suspect that protecting the European car industry is part of the explanation.</p>

<p>The minister presents this as a pure safety decision. But notice that the countries that approved FSD first, the Netherlands, Lithuania, Estonia, Denmark, and Belgium, have one thing in common: <strong>none of them has a major national car brand to protect</strong>.</p>

<h2 id="you-need-data-to-train-ai">You need data to train AI</h2>

<p>The car industry and the minister are overlooking something fundamental about AI: <strong>you need data to train it on</strong>.</p>

<p>In October 2016, almost ten years ago, Tesla announced that every car leaving the factory would be equipped with eight cameras and the hardware for full self-driving. Tesla has been collecting real-world driving data ever since. Its camera-equipped fleet has driven billions of kilometers, and Tesla can select and upload the real-world driving clips that are useful for training. It does not need to transmit every kilometer to learn from the fleet.</p>

<p>European car makers also collect driving data. But none of them combines Tesla’s fleet scale, standardized hardware, and nearly a decade of continuous deployment. Without comparable data, I do not see how they will catch up.</p>

<p>In the video, the minister reminds us that France has some of the best engineers in the world. That is true.</p>

<p>But this is not a traditional engineering problem. You cannot specify, design, and engineer your way to autonomous driving.</p>

<blockquote>
  <p>Without data, there is no AI. And you cannot buy ten years of driving data. You have to collect it.</p>
</blockquote>

<h2 id="this-only-concerns-a-few-cars-anyway">This only concerns a few cars anyway</h2>

<p>The current European rollout only concerns the newer Tesla models with <strong>Hardware 4</strong>. And the customer has to pay a subscription of <strong>99 euros per month</strong> to activate FSD.</p>

<p>That will further limit the number of users actually activating FSD in France. We are not talking about millions of cars suddenly driving themselves. We are talking about a small, careful, and paying group of early adopters.</p>

<h2 id="the-driver-is-responsible">The driver is responsible</h2>

<p>This is important, and the minister actually says it himself: <strong>FSD (Supervised) is a driver assistance system, not an autonomous driving system</strong>.</p>

<p>The driver has to be in the driver’s seat, pay attention, and be ready to intervene at any moment. The driver remains fully responsible, including legally, even when FSD is activated.</p>

<p>So the safety net the minister is asking for is already there. It is the driver.</p>

<h2 id="once-approved-the-customers-will-move">Once approved, the customers will move</h2>

<p>In the <a href="https://stockanalysis.com/stocks/tsla/transcripts/653184-q2-2026/">Q2 2026 earnings call</a>, held the very same day as the minister’s video, Elon Musk said:</p>

<blockquote>
  <p>We’re seeing in locations that have FSD approved, we’re seeing a very high take rate of FSD. In fact, I think for a lot of people, they’re actually buying Tesla Full Self-Driving with a car attached, as opposed to a car with FSD.</p>
</blockquote>

<p>Customers are walking into the stores asking for Full Self-Driving, with whatever car it comes with. About 55% of North American deliveries in Q2 had an FSD subscription enabled at the time of delivery.</p>

<p>This supports the point. <strong>Once FSD is approved in Europe, the rest of the car industry will lose even more customers to Tesla.</strong> The French car industry knows it. The German car industry knows it. And I suspect the minister knows it too.</p>

<p>Delaying the approval does not change this. It only delays the moment the European car industry has to face it.</p>

<h2 id="the-ministers-answer-full-transcript-in-english">The minister’s answer (full transcript in English)</h2>

<p>Here is the full transcript of Philippe Tabarot’s video answer, translated into US English with my comments.</p>

<blockquote>
  <p>Hello everyone, I am very happy to answer today a question that has attracted a lot of interest: Ludovic’s question about Tesla’s FSD driver assistance system.</p>

  <p>So that everyone understands what this is about: this system is a driver assistance system developed by Tesla for its vehicles. It is a very advanced driver assistance system whose goal is to drive in all environments, executing all maneuvers automatically and driving hands-free. Until now, these driver assistance systems were mostly oriented toward highways, and in general, at least one hand must remain on the steering wheel. So this is a significant change.</p>
</blockquote>

<p>At least Tesla is getting the credit that their system is very advanced. Well, they spent billions of dollars over 10 years on a bold prediction.</p>

<blockquote>
  <p>The Netherlands has decided to depart from the international and European regulatory framework and authorize its use on their territory.</p>
</blockquote>

<p>Unbelievable. Mr. Tabarot is criticizing the Netherlands for moving forward. For reference, see the
<a href="https://www.rdw.nl/en/news/2026/rdw-explanation-of-european-type-approval-tesla-with-provisional-validity-in-the-netherlands">RDW explanation of European type approval Tesla with provisional validity in the Netherlands</a>.</p>

<blockquote>
  <p>In France, we consider that this system brings a certain number of technological advances, but that the trade-offs in terms of safety are not yet sufficient for an authorization as it stands. Especially since this system is not an autonomous driving system: the driver remains responsible for driving the vehicle at all times, including when FSD is activated.</p>
</blockquote>

<p>This is strange and in contradiction to the RDW: “<em>Safety is RDW’s top priority.</em>” and “<em>We have thoroughly examined and verified this system for more than one and a half years.</em>”</p>

<blockquote>
  <p>Moreover, several other European countries share these reservations about the system’s level of safety. Our position is therefore not isolated.</p>
</blockquote>

<p>Why is it necessary to emphasize that France is not alone? This is completely irrelevant.</p>

<blockquote>

  <p>So what are these concerns? Without going into overly technical details, there are several. First example: we have found that the system allows speeding. If it notices that the vehicles around it are driving faster, in plain terms, it can drive at 70 km/h instead of 50 if traffic is moving at that speed, and this without any explicit request from the driver. In some cases, a vehicle can thus go up to 50% above the authorized speed limit. The rules of the road are therefore not respected.</p>
</blockquote>

<p>I find this argument absurd, and much of the reaction on social media has been the same. The system acts human-like, drives like anyone else would, yet you argue that FSD should “police” the other drivers. Nonsense. It makes no sense to make such requirements.</p>

<blockquote>
  <p>Another example: according to the data we have, the system does not guarantee an optimal level of driver attention in the city and during the most complex maneuvers, such as lane changes, crossing intersections, or roundabouts. Yet a lack of attention can have very serious consequences, both for people outside the vehicle and for its occupants. And let me remind you: even when the driver assistance system is activated, the driver remains criminally liable in the event of an accident.</p>
</blockquote>

<p>We are talking about FSD <strong>Supervised</strong>. The whole point is that the driver is responsible and present.</p>

<p>Philippe Tabarot says no to Tesla FSD on safety grounds, while he has earlier acknowledged that drug use is a growing problem among drivers in France.</p>

<p>To understand this, I invite you to read a post from Philippe Tabarot on LinkedIn one year ago.</p>

<p><a href="https://www.linkedin.com/feed/update/urn:li:activity:7323410878865764354/">Il y a 3 mois jour pour jour, Joana, 15 ans, perdait la vie à Châteaudun dans un accident de car scolaire.</a></p>

<blockquote>
  <p>Exactly three months ago today, 15-year-old Joana lost her life in Châteaudun in a school bus accident.</p>

  <p>The driver’s positive cannabis test revealed an unacceptable reality: drug use, a scourge in our society, can also affect those responsible for transporting our country’s primary school pupils and middle and high school students.</p>

  <p>This is unacceptable for families who place their children, in complete trust, in the care of school transport professionals.</p>
</blockquote>

<p>Would you rather put your kid in a French school bus or in an autonomous Tesla?</p>

<p><a href="https://www.tf1info.fr/societe/transports-scolaires-deja-30-000-controles-realises-depuis-le-debut-de-l-annee-annonce-le-ministere-2392641.html">By the end of August 2025, nearly 30.000 school bus drivers had been checked.</a></p>

<ul>
  <li>119 tested positive for drugs;</li>
  <li>63 tested positive for alcohol;</li>
  <li>337 other offenses were reported.</li>
</ul>

<p><strong>These people are driving your kids to school!</strong> These are not average drivers.</p>

<blockquote>
  <p>So what are the next steps? We are currently continuing our technical discussions with the Netherlands, the other European states, and Tesla to refine our analysis. Last week, a representative of the Ministry of Transport actually traveled to the Netherlands.</p>
</blockquote>

<p>Wow. One person went to the Netherlands. I am <strong>not</strong> impressed.</p>

<blockquote>
  <p>I want to emphasize this: France supports these technologies, and their deployment must take place within a framework of trust that makes it possible to evaluate the systems and determine the adjustments to FSD needed to guarantee a high level of safety.</p>
</blockquote>

<p>I am sure they do.</p>

<blockquote>
  <p>Ludovic, I hope these explanations help you better understand the ongoing debates. At a time when the number of road accidents is rising again, my responsibility is to enable innovation without ever compromising on safety. Driver assistance systems as advanced as Tesla’s are new because they are designed to operate in all driving environments, including in the city, where situations are particularly complex. It is therefore normal that we take the time to evaluate them properly, to protect the driver as well as all road users.</p>
</blockquote>

<p>I think there are some important observations here:</p>

<ol>
  <li>How many Teslas with FSD activated would actually be driving around in France? Tesla is a fairly expensive car in France, and paying 99 € per month for the feature will definitely hold some back. Also remember that only the newer cars with Hardware 4 can activate the option.</li>
  <li>We can conclude that only fairly rich people with newer cars will actually buy it, and eventually use it some of the time.</li>
  <li>So how high is the actual risk that Tesla FSD causes an accident compared with all the accidents happening in France?</li>
</ol>

<p>See last section.</p>

<blockquote>
  <p>France has always supported the deployment of new driver assistance technologies. We have already granted exemptions in the past for this type of system when it met the safety requirements. This is the case, for example, with Ford and, recently, BMW. And we must look even further ahead: the goal is to move from driver assistance to autonomous driving. In France, more than 200 autonomous vehicle experiments have been carried out since 2015. That is a solid foundation, but I want to go further and enable the arrival of type-approved autonomous vehicles in France as early as 2027.</p>
</blockquote>

<p>So we have to wait until next year.</p>

<blockquote>
  <p>It is in this spirit that France, together with Germany, Luxembourg, and 15 other countries, has launched the European test site initiative for autonomous vehicles. The goal is to harmonize the rules between countries and enable their large-scale deployment across Europe in complete safety.</p>
</blockquote>

<p>This is great news. Actually, the only thing to be excited about.</p>

<blockquote>
  <p>In the fall, I will bring together the French autonomous vehicle ecosystem to present the levers that will accelerate the deployment of this technology and set a course for the coming years.</p>
</blockquote>

<p>Now this is interesting. He is delaying Tesla FSD until next year and gathering the French auto industry in the fall. I will be looking forward to seeing what comes out of this.</p>

<blockquote>
  <p>In France, we are fortunate to have world-class car manufacturers and automotive suppliers, but also some of the best engineers in the world in the field of artificial intelligence and digital technology. We have a real card to play, and we will be there, but never at the expense of safety on our roads.</p>
</blockquote>

<p>And in the end, I believe this is what it is all about: the French automotive industry is behind.</p>

<p>Yes. France has world-class engineering universities and Grandes Écoles. France has engineers and mathematicians. France has <a href="https://mistral.ai/">Mistral.ai</a>. France might even find some money, and it has electricity.</p>

<h2 id="what-fsd-is-up-against-the-french-driver">What FSD is up against: the French driver</h2>

<p>The French government publishes an annual report on traffic violations. The
<a href="https://www.onisr.securite-routiere.gouv.fr/etat-de-linsecurite-routiere/bilans-annuels-infractions-et-permis-points/bilan-2024-des-infractions-au-code-de-la-route">Bilan 2024 des infractions au code de la route</a>
from the national road safety observatory (ONISR) gives the reader a true picture of what FSD is up against: the human French driver.</p>

<p>In 2024 alone:</p>

<ul>
  <li>More than <strong>27,6 million traffic violations</strong> were recorded.</li>
  <li>Approximately <strong>1,1 million of them were criminal road offenses</strong> (<em>délits</em>), an increase of 9,5% from 2023. Of these, 25% concerned driving without insurance, 18% hit-and-run (<em>délit de fuite</em>), and 14% driving without a license or with an invalid license.</li>
  <li><strong>13,9 million automated speeding notices</strong> were issued.</li>
  <li>More than <strong>270.000 automated notices</strong> were issued for running red lights or railway-crossing signals.</li>
  <li><strong>11,7 million license points were withdrawn</strong>: 5,7 million for speeding, 1,9 million for failure to respect priority rules, and 1,7 million for using a phone or earphones behind the wheel.</li>
</ul>

<p>And here is the number that should keep the minister awake at night: only 79% of all French drivers still have all 12 points on their license. Among the presumed perpetrators of fatal accidents, only 49% did.</p>

<p>It gets worse. In 2023, traffic offenses accounted for <strong>42% of all criminal convictions in France</strong>, and 58% of those involved driving under the influence of alcohol and/or drugs.</p>

<p>And who are these convicted drivers? The report answers that too. In 2023, 205.536 men and 21.473 women were convicted of traffic offenses. <strong>91% of all convictions concerned men</strong>, and 52% concerned men between 18 and 34 years old. Men account for 92,4% of the convictions for driving under the influence of drugs, 91,9% for driving despite a suspended license, and 88,2% for drunk driving. The age profile is just as clear: driving without a license peaks among the 18-24 year olds, driving without insurance and driving despite a suspension peak among the 25-34 year olds, and drunk driving is concentrated among the 25-44 year olds.</p>

<p>FSD has no gender and no age. It does not turn 19 and start showing off.</p>

<p>Now go back and read the minister’s main argument again: FSD might drive 70 km/h in a 50 zone if the surrounding traffic does. The surrounding traffic. The one collecting 13,9 million speeding notices a year.</p>

<blockquote>
  <p>FSD does not drink. It does not take drugs. It does not text behind the wheel. It does not lose its license. And it does not deliberately flee the scene of an accident. FSD can make errors, but not for the same reasons as human drivers.</p>
</blockquote>

<p>This is my answer to the third observation above. The question is not whether FSD is perfect. The question is whether it is better than this.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I want to give the minister, Philippe Tabarot, credit for one thing: he answered a citizen’s question, on camera, with arguments. That is how a democracy should work.</p>

<p>I am also happy that the EU is harmonizing traffic rules. It will benefit everyone.</p>

<p>But EU bureaucracy does not advance technology. It has never invented anything. It makes rules.</p>

<p>As the joke goes:</p>

<ul>
  <li>America innovates</li>
  <li>China copies</li>
  <li><a href="https://artificialintelligenceact.eu/">Europe regulates</a></li>
</ul>

<p>The old continent is living up to its reputation once again.</p>]]></content><author><name>Finn</name></author><category term="Politics" /><summary type="html"><![CDATA[France says no to Tesla FSD (for now)]]></summary></entry><entry><title type="html">There is a new sheriff in town (Business Central)</title><link href="https://www.finnpedersenfrance.com/programming/2026/07/22/there-is-a-new-sheriff-in-town.html" rel="alternate" type="text/html" title="There is a new sheriff in town (Business Central)" /><published>2026-07-22T07:00:00+02:00</published><updated>2026-07-24T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2026/07/22/there-is-a-new-sheriff-in-town</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2026/07/22/there-is-a-new-sheriff-in-town.html"><![CDATA[<h2 id="there-is-a-new-sheriff-in-town">There is a new sheriff in town</h2>

<p><img src="/assets/images-01/NewSheriffInTownV2.png" alt="Robot Sheriff and Dinosaur in jail" /></p>

<p><a href="https://github.com/StefanMaron/BusinessCentral.LinterCop">BusinessCentral.LinterCop</a> is deprecated and <a href="https://alcops.dev/">ALCops</a> is the new sheriff in town.</p>

<p>Well, actually, there are six:</p>

<table>
  <thead>
    <tr>
      <th>Analyzer</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/applicationcop/">ApplicationCop</a></td>
      <td>Enforces Business Central application conventions for tables, pages, enums, labels, and permissions.</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/documentationcop/">DocumentationCop</a></td>
      <td>Validates that AL code is properly documented with comments and XML documentation.</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/formattingcop/">FormattingCop</a></td>
      <td>Enforces consistent code formatting and visual structure.</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/lintercop/">LinterCop</a></td>
      <td>Flags code quality issues, measures complexity, and promotes modern AL patterns.</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/platformcop/">PlatformCop</a></td>
      <td>Detects code that is technically broken, dangerous, or silently ignored at the AL platform level.</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/testautomationcop/">TestAutomationCop</a></td>
      <td>Validates the structure and correctness of AL test code.</td>
    </tr>
  </tbody>
</table>

<h2 id="motivation">Motivation</h2>

<p>If you need to know more about the motivation for this change, you can find the explanation in Stefan Maron’s article
<a href="https://stefanmaron.com/posts/introducing-alcops/">Introducing ALCops</a>
and watch this video on YouTube:
<a href="https://www.youtube.com/watch?v=tMqCSibSRug">ALCops: LinterCop’s Successor for Business Central AL Code Analysis</a></p>

<h2 id="migration-from-businesscentrallintercop-to-alcops">Migration from BusinessCentral.LinterCop to ALCops</h2>

<p>I have made an enhanced version of the <strong>LinterCop Migration Script</strong>, which you can find on GitHub here: <a href="https://github.com/finnpedersenkazes/LinterCop-Migration-Script">https://github.com/finnpedersenkazes/LinterCop-Migration-Script</a>.</p>

<p>Original: <a href="https://alcops.dev/docs/lintercop-migration-script/">https://alcops.dev/docs/lintercop-migration-script/</a></p>

<h2 id="installation">Installation</h2>

<p>Find the VS Code extension on the <a href="https://marketplace.visualstudio.com/items?itemName=Arthurvdv.alcops">Visual Studio Code Marketplace</a>.</p>

<p>Or install it directly from Visual Studio Code.</p>

<p>Source: <a href="https://alcops.dev/docs/getting-started/vscode/">Getting Started</a></p>

<blockquote>
  <p>Don’t forget to uninstall the <strong>BusinessCentral.LinterCop</strong> extension.</p>
</blockquote>

<h2 id="setup">Setup</h2>

<p>Look for <strong>ALCops</strong> in the menu bar at the bottom of VS Code or open the Command Palette (<code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code>) and search for ALCops. Then select <em>ALCops: Select Code Analyzers</em>.</p>

<h3 id="settingsjson">settings.json</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"al.enableCodeAnalysis"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"al.codeAnalyzers"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"${CodeCop}"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${UICop}"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${PerTenantExtensionCop}"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.ApplicationCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.DocumentationCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.FormattingCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.LinterCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.PlatformCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.TestAutomationCop.dll"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"${analyzerFolder}ALCops.Common.dll"</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="nl">"alcops.automaticUpdates"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"alcops.updateNotification"</span><span class="p">:</span><span class="w"> </span><span class="s2">"notify-only"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"alcops.versionChannel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stable"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"alcops.checkUpdateInterval"</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="p">,</span><span class="w">
    </span><span class="nl">"al.backgroundCodeAnalysis"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Project"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"al.browser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Edge"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"al.ruleSetPath"</span><span class="p">:</span><span class="w"> </span><span class="s2">"../.BlueCircle360Certified/Level-4/Main.ruleset.json"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p>Don’t forget to remove the old <strong>BusinessCentral.LinterCop</strong> settings.</p>
</blockquote>

<h2 id="what-is-new">What is new?</h2>

<p>My code is normally warning-free.</p>

<p>So what did <strong>ALCops</strong> detect in my recent project?</p>

<table>
  <thead>
    <tr>
      <th>Rule ID</th>
      <th>Description</th>
      <th>Category</th>
      <th>Code Fix</th>
      <th>Severity</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/platformcop/pc0037/">PC0037</a></td>
      <td>Use Validate() instead of direct field assignment</td>
      <td>Design</td>
      <td>Yes</td>
      <td>Warning</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/platformcop/pc0030/">PC0030</a></td>
      <td>Use partial records on read operation</td>
      <td>Performance</td>
      <td>Yes</td>
      <td>Info</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/platformcop/pc0029/">PC0029</a></td>
      <td>Use CreateSequentialGuid for key fields</td>
      <td>Performance</td>
      <td>Yes</td>
      <td>Info</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/documentationcop/dc0007/">DC0007</a></td>
      <td>Public objects must include XML documentation comments</td>
      <td>Design</td>
      <td>No</td>
      <td>Info</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/documentationcop/dc0009/">DC0009</a></td>
      <td>Events must include XML documentation comments</td>
      <td>Design</td>
      <td>No</td>
      <td>Hidden</td>
    </tr>
    <tr>
      <td><a href="https://alcops.dev/docs/analyzers/applicationcop/ac0032/">AC0032</a></td>
      <td>Unused permission declared</td>
      <td>Design</td>
      <td>Yes</td>
      <td>Info</td>
    </tr>
  </tbody>
</table>

<p>I have now started reviewing these cases to better understand what should be done.</p>

<h2 id="conclusion">Conclusion</h2>

<p><strong>AL code analysis</strong> helps you keep your code cleaner, safer, and faster.</p>

<p>It helps you find problems early, which will save you time and money.</p>

<p>But more importantly, it creates a coding standard for your team, for now and in the future.</p>

<blockquote>
  <p>Clean Code is about respect for the developer coming after you.</p>
</blockquote>

<p>Reach out to me if you need help getting started with AL code analysis.</p>

<h2 id="observations">Observations</h2>

<h3 id="fc0004-permission-declarations-should-be-ordered-alphabetically">FC0004 Permission declarations should be ordered alphabetically</h3>

<p>I ended up disabling FC0004 in my permissionset.al files because it conflicted with auto save.</p>

<h3 id="lc0095-parameter-is-not-referenced">LC0095 Parameter is not referenced</h3>

<p>LC0095 had to be disabled in EventSubscriber Codeunits because most functions don’t use all parameters.</p>

<h3 id="pc0037-use-validate-instead-of-direct-field-assignment">PC0037 Use Validate() instead of direct field assignment</h3>

<p>PC0037 forced me to move all TableRelation properties from Table Fields to Page Fields, which probably is a good thing.</p>

<p>The <strong>TableRelation</strong> property provides two things. Control that the value is already in the related table and a lookup link. I needed the lookup link, so I moved the property to the field on the page. I am <strong>not</strong> testing the page.</p>

<p>I am writing <a href="/programming/2025/06/26/temporary-tables-in-tests.html">super fast tests using Temporary tables</a>.
Using Validate() everywhere collided with checking Table Relations on the table.
Since I aime for 100% code coverage of the objects I test,
I moved the Table Relation property to the page.</p>

<p>I prioritized to keep my tests simple and fast, at the expense of having to keep track of the
Table Relation on both the Card Page and the List Page. I hope I made the right decision.</p>

<h2 id="recommendations">Recommendations</h2>

<h3 id="how-to-disable-a-rule-in-a-special-case">How to disable a rule in a special case</h3>

<p>Sometimes you have to accept an antipattern or wait for the base application to catch up.</p>

<blockquote>
  <p>LC0088: Prefer Enum over Option type</p>
</blockquote>

<p>In this case, the base function only accepts an option. For readability, I have given it one.</p>

<pre><code class="language-al">#pragma warning disable LC0088
    [NonDebuggable]
    internal procedure CreateHash(TextJson: Text; SecretKey: SecretText) Hash: SecretText
    var
        CryptographyManagement: Codeunit System.Security.Encryption."Cryptography Management";
        HashAlgorithmType: Option HMACMD5,HMACSHA1,HMACSHA256,HMACSHA384,HMACSHA512;
    begin
        Hash := CryptographyManagement.GenerateHash(TextJson, SecretKey, HashAlgorithmType::HMACSHA256);
    end;
#pragma warning restore LC0088
</code></pre>

<h3 id="event-subscriber-codeunit">Event Subscriber Codeunit</h3>

<p>Always keep your Event Subscribers in a dedicated Codeunit.</p>

<blockquote>
  <p>LC0095: Parameter is not referenced</p>
</blockquote>

<p>Typically, you are not using all the parameters the Event Publisher is offering,
therefore you need to disable the rule.</p>

<p>Start and end your object file with a <code class="language-plaintext highlighter-rouge">#pragma warning</code> to <code class="language-plaintext highlighter-rouge">disable</code> the rule for the entire Codeunit.</p>

<pre><code class="language-al">#pragma warning disable LC0095

codeunit 50000 "Event Subscribers"
{
...
}

#pragma warning restore LC0095
</code></pre>

<h2 id="clean-al-code-initiative">Clean AL Code Initiative</h2>

<ul>
  <li>Part 1: <a href="/programming/2023/10/10/BC-Rulesets.html">Rulesets in Business Central</a></li>
  <li>Part 2: <a href="/programming/2023/12/28/Namespaces-in-AL.html">Namespaces in AL</a></li>
  <li>Part 3: <a href="/programming/2023/12/29/Extensions-for-AL.html">VS Code Extensions for AL</a></li>
  <li>Part 4: <a href="/programming/2024/01/03/Automated-Tests-in-AL.html">Automated Tests in AL</a></li>
  <li>Part 5: <a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced CodeCop Analyzer and Custom Rulesets</a></li>
  <li>Part 6: <a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
  <li>Part 7: <a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
  <li><strong>Part 8</strong>: <a href="/programming/2026/07/22/there-is-a-new-sheriff-in-town.html">There is a new sheriff in town</a></li>
</ul>

<h2 id="super-fast-tests-covering-100-of-your-code">Super fast tests covering 100% of your code</h2>

<ul>
  <li>Part 1: <a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces - Part 1 - System Environment</a></li>
  <li>Part 2: <a href="/programming/2025/06/26/environment-interface-part-2.html">Environment Interfaces - Part 2 - External API</a></li>
  <li>Part 3: <a href="/programming/2025/06/26/environment-interface-part-3.html">Environment Interfaces - Part 3 - Standard Application</a></li>
  <li>Part 4: <a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a></li>
</ul>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[There is a new sheriff in town]]></summary></entry><entry><title type="html">The urgent need to modernize Business Central development</title><link href="https://www.finnpedersenfrance.com/programming/2025/11/30/urgent-need-to-modernize-bc-development.html" rel="alternate" type="text/html" title="The urgent need to modernize Business Central development" /><published>2025-11-30T06:00:00+01:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/11/30/urgent-need-to-modernize-bc-development</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/11/30/urgent-need-to-modernize-bc-development.html"><![CDATA[<h2 id="the-urgent-need-to-modernize-business-central-development">The urgent need to modernize Business Central development</h2>

<p><img src="/assets/images-01/DinosaurAIandBCnew.png" alt="Dinosaur AI and Business Central" /></p>

<h2 id="we-are-at-an-inflection-point">We are at an inflection point</h2>

<p>I am just back from <a href="https://www.directionsforpartners.com/emea2025">Directions EMEA 2025</a> where I have spoken with a number of people who show the way forward.</p>

<p>Maybe the inflection point was there before, but AI has definitely made it more obvious.</p>

<blockquote>
  <p><strong>Artificial Intelligence</strong> is the <em>asteroid</em> hurtling down and hitting our ecosystem.</p>
</blockquote>

<h2 id="business-central-is-fundamentally-different-from-dynamics-nav">Business Central is fundamentally different from Dynamics NAV</h2>

<blockquote>
  <p>It is an error not to make enough sense of urgency.</p>
</blockquote>

<p>This is not just about upgrading from Dynamics NAV to BC. <strong>Business Central development is fundamentally different</strong> from Dynamics NAV development, and this difference is accelerating.</p>

<h3 id="the-upgrade-cycle-has-changed-forever">The upgrade cycle has changed forever</h3>

<p>In Dynamics NAV, you could upgrade every five or ten years. You had time. You could write code that worked, even if it didn’t follow best practices. The upgrade cycle was slow enough that technical debt could accumulate without immediate consequences.</p>

<p><strong>Business Central changes the rules completely:</strong></p>

<ul>
  <li><strong>Monthly updates:</strong> Your code is updated every month, whether you like it or not</li>
  <li><strong>Breaking changes:</strong> With two major releases per year, breaking changes arrive on a regular basis</li>
</ul>

<h3 id="the-problem-isnt-newits-just-more-urgent">The problem isn’t new—it’s just more urgent</h3>

<p>The truth is, Dynamics NAV had programming guidelines. Microsoft provided them. But many developers created their own patterns, and it worked because upgrades were rare.</p>

<p><strong>That approach is much harder to sustain now.</strong></p>

<p>When you’re facing monthly updates and breaking changes, code that doesn’t follow modern practices becomes a challenge. It can break, cost more to fix, and frustrate customers. And it may put you at a competitive disadvantage.</p>

<h2 id="the-customer-perspective">The customer perspective</h2>

<p>Customers are often unaware of the technical debt hidden in their codebase. They don’t know to ask for:</p>

<ul>
  <li>Code that follows Microsoft’s guidelines</li>
  <li>Test coverage</li>
  <li>Modern architecture patterns</li>
  <li>Regular code reviews</li>
</ul>

<p>They trust that “senior developers with a lot of experience” are delivering quality code. But experience without modern practices can sometimes lead to legacy code.</p>

<h2 id="the-ai-acceleration">The AI acceleration</h2>

<p>The world is moving fast. AI tools are making it possible to build better solutions faster. Competitors who adopt modern practices and leverage AI will outcompete those who don’t.</p>

<p>If you’re still writing code the Dynamics NAV way, you may find yourself falling further behind every month.</p>

<h2 id="what-modern-business-central-development-looks-like">What modern Business Central development looks like</h2>

<p>Modern BC development requires:</p>

<ul>
  <li><strong>Investment in training:</strong> Understanding BC’s architecture, not just porting Dynamics NAV code</li>
  <li><strong>Test-driven development:</strong> Writing tests that run in milliseconds (see my articles on <a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces</a> and <a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a>)</li>
  <li><strong>Code review:</strong> Peer review of all changes (see my article on <a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a>)</li>
  <li><strong>Modern tools:</strong> CodeCop, linters, proper settings.json configuration (see my article on <a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced Rulesets</a>)</li>
  <li><strong>Clean architecture:</strong> Namespaces, interfaces, proper organization (see my <a href="/programming/2023/10/10/BC-Rulesets.html">Clean Code Initiative</a>)</li>
  <li><strong>Engineering management:</strong> Teams that understand modern software development</li>
</ul>

<h2 id="the-cost-of-delay">The cost of delay</h2>

<blockquote>
  <p>We’ll upgrade to BC during the next two years.</p>
</blockquote>

<p>But in two years, everything will have moved so far from where we are now. This year alone has seen rapid changes. Waiting two years means you’ll be even further behind, with even more technical debt to address.</p>

<p><strong>The time to act is now.</strong></p>

<h2 id="how-to-identify-legacy-code-patterns">How to identify legacy code patterns</h2>

<p>After three years as a freelance BC developer, I’ve seen patterns that suggest code may not be ready for BC’s update cycle:</p>

<ul>
  <li>Objects organized by Object Type instead of functionality</li>
  <li>Global variables everywhere</li>
  <li>No test apps</li>
  <li>Minimal or missing settings.json configuration</li>
  <li>No CodeCop or other linters</li>
  <li>No use of interfaces or namespaces</li>
  <li>Inconsistent naming conventions</li>
  <li>No automatic code cleanup</li>
  <li>No peer code review process</li>
</ul>

<p>If this describes your codebase, it is urgent time to get started fixing it.</p>

<h2 id="the-path-forward">The path forward</h2>

<p>I have changed my job title to <a href="https://www.linkedin.com/in/finnpedersen/">Modern Business Central Developer</a>.</p>

<blockquote>
  <p>Prefer to rewrite code using modern patterns rather than patching old code.</p>
</blockquote>

<p>If you have adopted a modern development mindset, then show it by changing your job title to <strong>Modern Business Central Developer</strong>.</p>

<p>And, please, by all means share this article with your network and ask your network to join the movement.</p>

<h2 id="we-need-to-outcompete-legacy-practices">We need to outcompete legacy practices</h2>

<p>The best way to make legacy practices obsolete is to outcompete them.</p>

<blockquote>
  <p>By using the latest technologies and a modern mindset, we will build <strong>better solutions</strong> faster.</p>
</blockquote>

<p>Solutions that are:</p>

<ul>
  <li>Easy to maintain</li>
  <li>Prepared for evolution</li>
  <li>Easy to extend</li>
  <li>Easy to support</li>
  <li>Ready for BC’s update cycle</li>
</ul>

<p>Reach out if you need help with the transition.</p>

<p>Finn Pedersen</p>

<p>Modern BC Developer</p>

<hr />

<blockquote>
  <p>PS: I realize that some people might be offended by this post. That was not the intention. The purpose of this post is not to offend anyone, but to create enough sense of urgency. The technical reality is that BC’s update cycle requires modern practices. The longer we delay, the more expensive and difficult the transition becomes. I want to help, not criticize.</p>
</blockquote>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[The urgent need to modernize Business Central development]]></summary></entry><entry><title type="html">Vibe Coding with Lovable.dev (No Code)</title><link href="https://www.finnpedersenfrance.com/programming/2025/10/15/vibe-coding-with-lovable-dev.html" rel="alternate" type="text/html" title="Vibe Coding with Lovable.dev (No Code)" /><published>2025-10-15T07:00:00+02:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/10/15/vibe-coding-with-lovable-dev</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/10/15/vibe-coding-with-lovable-dev.html"><![CDATA[<h2 id="vibe-coding-with-lovabledev">Vibe Coding with Lovable.dev</h2>

<h2 id="an-app-says-more-than-a-1000-words">An app says more than a 1000 words</h2>

<p><strong>If you are not a developer, you have to try Lovable.dev!</strong></p>

<p>The reason is that it will expand your view on what is possible.</p>

<p>This is what you will learn from making apps with <code class="language-plaintext highlighter-rouge">Lovable.dev</code>.</p>

<ul>
  <li>How to express an idea</li>
  <li>How to reformulate and clarify your thoughts</li>
  <li>How to communicate your idea to others</li>
  <li>Understand how your first hypotheses had flaws and can be improved</li>
</ul>

<p>To help you get started, there are some examples below.</p>

<h2 id="lovabledev">Lovable.dev</h2>

<p>Use my referral link to get free credits when you sign up.</p>

<p><a href="https://lovable.dev/invite/8e75e0e5-5de0-4a7f-860d-3f192c0714f1">https://lovable.dev/invite/8e75e0e5-5de0-4a7f-860d-3f192c0714f1</a></p>

<p><em>And don’t forget to publish your app.</em>
It will give me 10 credits to build more fun games.</p>

<h2 id="team-building">Team Building</h2>

<p><strong>I recommend vibe coding for team building.</strong></p>

<p>People who don’t know how to code often have fewer ideas for what is possible to make. No longer.</p>

<p>After a few attempts you will rapidly get the hang of it.</p>

<h2 id="the-hottest-new-programming-language-is-english">The hottest new programming language is English</h2>

<p>I will be giving a talk on Vibe Coding at the <strong>Directions EMEA 2025</strong> conference, 4-6 November 2025 in Poznan, Poland.</p>

<p>More information about the conference at <a href="https://www.directionsforpartners.com/emea2025">www.directionsforpartners.com/emea2025</a>
and my <a href="https://sessionize.com/s/finn-pedersen/vibe-coding.-the-hottest-new-programming-language-/142123">session</a>.</p>

<h2 id="snake-game">Snake Game</h2>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Build a Snake game for my phone as a web app
</code></pre></div></div>

<h3 id="extra-instructions">Extra instructions</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The game must not end. When the snake reaches the edge it should just continue on the other side. Start with a length of 3. Add buttons on the right hand side. The swiping up and right did not work as expected.

Make the head of the snake blue.
</code></pre></div></div>

<p><a href="https://mobile-snake-play.lovable.app">mobile-snake-play.lovable.app</a></p>

<p><img src="/assets/images-01/LovableSnakeGame.png" alt="Snake Game" /></p>

<h2 id="converting-french-to-braille">Converting French to Braille</h2>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Make a small web app that converts a text as I type it to braille. I want the braille to be above the text input field so I can see it as I type on my phone. The braille should not be abbreviated. I expect to write in French. ...
</code></pre></div></div>

<p><a href="https://braille-ahead-typer.lovable.app">braille-ahead-typer.lovable.app</a></p>

<p><img src="/assets/images-01/LovableConvertBraille.png" alt="Braille Converter" /></p>

<h2 id="2048-game">2048 Game</h2>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Make a 2048 game that runs on my phone.
</code></pre></div></div>

<h3 id="extra-instruction">Extra instruction</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Swiping works left, right, and up. Swipe down refreshes the page.
</code></pre></div></div>

<p><a href="https://twenty-forty-eight-slide.lovable.app/">twenty-forty-eight-slide.lovable.app</a></p>

<p><img src="/assets/images-01/Lovable2048game.png" alt="2048 Game" /></p>]]></content><author><name>Finn</name></author><category term="programming" /><summary type="html"><![CDATA[Vibe Coding with Lovable.dev]]></summary></entry><entry><title type="html">Vibe Coding with Cursor (Coding with AI)</title><link href="https://www.finnpedersenfrance.com/programming/2025/07/24/vibe-coding-with-cursor.html" rel="alternate" type="text/html" title="Vibe Coding with Cursor (Coding with AI)" /><published>2025-07-24T09:00:00+02:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/07/24/vibe-coding-with-cursor</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/07/24/vibe-coding-with-cursor.html"><![CDATA[<h2 id="vibe-coding-with-cursor">Vibe Coding with Cursor</h2>

<p>Whether we are just coding with AI or entirely Vibe Coding, we face the same challenges.</p>

<p>The term <strong>Vibe Coding</strong> went viral in February 2025 and now everyone is talking about it or trying it out.</p>

<p>I have submitted a talk, <strong>Vibe coding. The hottest new programming language is English</strong>
on this subject, to <a href="https://www.directionsforpartners.com/emea2025">Directions EMEA 2025</a></p>

<p><strong>There is actually a lot we can learn from vibe coding:</strong></p>

<ul>
  <li>How do we communicate our needs to the AI?</li>
  <li>Is that similar to how we communicate with a developer?</li>
  <li>What is easy and what is difficult in programming?</li>
  <li>Are some programming languages more suited for development with AI than others?</li>
  <li>Do you need to be a developer to do Vibe Coding?</li>
  <li>How much coding do you actually need to know to succeed with vibe coding?</li>
  <li>Is vibe coding just for fun or can I use it in my work?</li>
</ul>

<h2 id="what-is-vibe-coding">What is Vibe Coding?</h2>

<p>Computer scientist Andrej Karpathy, a co-founder of OpenAI and former AI leader at Tesla, introduced the term vibe coding in February 2025. The concept refers to a coding approach that relies on LLMs, allowing programmers to generate working code by providing natural language descriptions rather than manually writing it.</p>

<p>Source <a href="https://en.wikipedia.org/wiki/Vibe_coding">Wikipedia Vibe Coding</a></p>

<h2 id="the-snake-game">The snake game</h2>

<p>Here is an idea for a prompt:</p>

<blockquote>
  <p>Make the snake game in Elm as we knew it from the old nokia phones.</p>
</blockquote>

<ul>
  <li>Download and install Cursor</li>
  <li>Setup your development environment</li>
  <li>Write your prompt</li>
</ul>

<h3 id="40-minutes">40 minutes</h3>

<p>Within 40 minutes, I had a running game on my computer. I was super optimistic. Take into account that I was also playing, I mean testing, the game during that time.</p>

<p><img src="/assets/images-01/ElmSnakeGame.png" alt="Snake Game" /></p>

<p>Then …</p>

<h3 id="several-days-later">Several days later</h3>

<p>The app was only running on my laptop and I wanted it to work on my phone.
This is where the troubles started.
I refused to touch the code. I had to prompt my way to the result.
However, at one point, I had to study the code and specifically ask it to change some of the CSS.</p>

<p>I also had to rollback the code completely a couple of times.</p>

<p><img src="/assets/images-01/ElmSnakeGameMobileFirst.png" alt="Snake Game" /></p>

<p><a href="https://finnpedersenkazes.github.io/elm-snake/">Try it</a></p>

<blockquote>
  <p><strong>Warning</strong>: It is quite addictive.</p>
</blockquote>

<h3 id="project-on-github">Project on GitHub</h3>

<p><a href="https://github.com/finnpedersenkazes/elm-snake">Elm Snake Game on GitHub</a></p>

<p>The main branch is the first desktop version and the mobile-first branch is the mobile-first version.
Feel free to star the project and clone it.</p>

<p>You can follow my dialog with the AI in the <a href="https://github.com/finnpedersenkazes/elm-snake/blob/master/cursor_create_a_hello_world_app_in_elm.md">cursor_create_a_hello_world_app_in_elm.md</a> file.</p>

<p>It is the entire dialog to get to the first version. You don’t want to see the second. It is too long.</p>

<p>The <code class="language-plaintext highlighter-rouge">readme.md</code> is also almost entirely generated with a few prompts stating the sections.</p>

<h2 id="cursorcom">Cursor.com</h2>

<p>I used <a href="https://cursor.com/">Cursor.com</a>, the AI Code Editor, to realize the project.</p>

<p>I did not write a single line of code, so I did not care about setting anything up.</p>

<h2 id="programming-languages">Programming Languages</h2>

<p>Are all programming languages born equal for AI peer programming and Vibe Coding?
They are not. Actually, this will challenge our common worldview on programming languages.</p>

<p>What if another language will challenge <code class="language-plaintext highlighter-rouge">C#</code> or <code class="language-plaintext highlighter-rouge">TypeScript</code> simply because the AI works better with it?
Could a small and rather unknown language like <code class="language-plaintext highlighter-rouge">Elm</code> suddenly spring into popularity because it works much better with AI?</p>

<p>Some really interesting articles have started to appear.</p>

<ul>
  <li><a href="https://interjectedfuture.com/elm-as-target-language-for-vibe-coding/">Elm as target language for vibe coding by Wil Chung</a></li>
  <li><a href="https://medium.com/@jonathan.demontalembert/the-ai-symbiote-why-deterministic-languages-are-the-future-of-coding-f4a5ce4916e7">The AI Symbiote: Why Deterministic Languages Are the Future of Coding by Le Jon</a></li>
</ul>

<p>This is the reason why I chose Elm for my first vibe programming project.</p>

<h3 id="elm">Elm</h3>

<p>A delightful language for reliable web applications.</p>

<p><a href="https://elm-lang.org/">elm-lang.org</a></p>

<p>Elm is a pure functional language for building interactive web applications.
The model-update-view architecture has all state management handled in a single model.
The strong static type system guarantees <strong>no runtime errors</strong>.
And I just love the helpful compiler messages and easy-to-read code.</p>

<blockquote>
  <p>If it compiles, it works.</p>
</blockquote>

<p>As it turns out, the AI in Cursor also likes the helpful error messages from Elm.</p>

<h4 id="try-elm">Try Elm</h4>

<p>There are a couple of ways to try out Elm. Here are a few.</p>

<ul>
  <li><a href="https://elm-editor.com/">elm-editor.com</a></li>
  <li><a href="https://ellie-app.com/new">ellie-app.com</a></li>
</ul>

<h3 id="other-languages-and-al">Other languages and AL</h3>

<p>Since the amount of open source code for a given language is also important for training the LLM,
the most popular languages like JavaScript, TypeScript, and Python have an advantage.</p>

<p>A purpose-specific language might also have some advantages. AL is purpose-specific.</p>

<h4 id="al-and-ai">AL and AI</h4>

<p>Age and how much a language changes plays a role. AL is old and has evolved a lot.
There is no tradition for open source projects on GitHub, best practices were not followed, and
the language has evolved a lot. For example, we no longer write <code class="language-plaintext highlighter-rouge">Find('-')</code> we write <code class="language-plaintext highlighter-rouge">FindFirst()</code>.
So the LLM has to discard what was good in the past and now considered bad.</p>

<p>Where <code class="language-plaintext highlighter-rouge">Elm</code> has been stable for the past five years.</p>

<h2 id="ai-assistance-helping-the-ai">AI assistance helping the AI</h2>

<p>When I had trouble with the Mobile-First UI of the Game, I found that the Inspect function in
Chrome has an AI assistance. I asked how to fix the issue and passed the advice
directly to the AI developer in Cursor. Nice!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Are you in a team?
Do you work with developers, project managers, product owners, users, or managers?</p>

<p><strong>Here is what you should do:</strong> Make a team-building event and vibe code something together.</p>

<p>You will learn much more than you could possibly imagine.</p>

<blockquote>
  <p><strong>Warning</strong>: You will have so much fun that you will have a hard time getting back to work.</p>
</blockquote>

<p>Have fun and learn</p>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[Vibe Coding with Cursor]]></summary></entry><entry><title type="html">Temporary Tables in Tests (Business Central)</title><link href="https://www.finnpedersenfrance.com/programming/2025/06/26/temporary-tables-in-tests.html" rel="alternate" type="text/html" title="Temporary Tables in Tests (Business Central)" /><published>2025-06-26T10:00:00+02:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/06/26/temporary-tables-in-tests</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/06/26/temporary-tables-in-tests.html"><![CDATA[<h2 id="temporary-tables-in-tests">Temporary Tables in Tests</h2>

<p>The theme for this series is <em>Super fast tests covering 100% of your code</em>.</p>

<p>This 2025 series of articles is a continuation of the 2024 series called the Clean Code Initiative.</p>

<p>Clean Code is a condition for writing good tests, but it does not guarantee code coverage by tests.</p>

<h2 id="scenario">Scenario</h2>

<p>The app we are building communicates with an external API.</p>

<p>We face several key requirements:</p>

<ul>
  <li>The app can only be in production mode when running in a production environment. If running in a sandbox, it must operate in test mode.</li>
  <li>Our tests must cover all code paths, including those only reachable in production mode.</li>
  <li>Our tests cannot call the actual API, but we still need to verify both the requests we send and how we decode the API responses.</li>
  <li>Tests must be extremely fast, with runtimes measured in milliseconds.</li>
  <li>Writing tests should be quick and cost-effective.</li>
  <li>We want to test only our own code, assuming that dependencies and external systems work as expected. We assume everyone else knows what they are doing</li>
</ul>

<blockquote>
  <p><strong>Temporary Tables</strong> will satisfy the need for speed and isolation from the database.</p>
</blockquote>

<h2 id="avoid-touching-the-database-in-tests">Avoid Touching the Database in Tests</h2>

<p>We have looked at Environment Interfaces and how they save us time and improve speed, but there is one more thing to avoid during tests: touching the database. Here, we have to think differently—we are not going to develop an interface toward the database, and we don’t have to.</p>

<p>If we want to run hundreds of tests in seconds, we have to change the way we write functions. While the Clean Code Initiative told you to stick to the standard patterns, we are now going to challenge this a little bit in some cases.</p>

<h2 id="pure-functions">Pure Functions</h2>

<p>First of all, we want to have <strong>pure functions</strong>—that is, functions with no side effects. Touching the database is a side effect.</p>

<p>This has important consequences for where we place database operations like <code class="language-plaintext highlighter-rouge">Record.Get()</code>, <code class="language-plaintext highlighter-rouge">Record.Modify()</code>, <code class="language-plaintext highlighter-rouge">Record.Insert()</code>, and <code class="language-plaintext highlighter-rouge">Record.FindSet()</code> in our code. In this project, they are now all at the top level.</p>

<h2 id="passing-records-by-reference">Passing Records by Reference</h2>

<p>To achieve fast, isolated tests, we use <strong>temporary tables</strong>, which run entirely in memory. When a function operates on a temporary record, you must pass that record <strong>by reference</strong> using the <code class="language-plaintext highlighter-rouge">var</code> keyword. This ensures that any changes made to the record inside the function are retained and that the function works with the same in-memory instance. In the example below, both <code class="language-plaintext highlighter-rouge">EncodePhoneNumber</code> and the helper method <code class="language-plaintext highlighter-rouge">GetPhoneNumber</code> take <code class="language-plaintext highlighter-rouge">Setup</code> as a <code class="language-plaintext highlighter-rouge">var</code> parameter, allowing the test to run against a temporary, in-memory version of the table instead of querying or modifying the actual database.</p>

<pre><code class="language-al">internal procedure EncodePhoneNumber(var Setup: Record Setup; ObjectKey: Text; PhoneNumber: Text; var Object: JsonObject)
var
    PhoneNumberAsText: Text;
begin
    PhoneNumberAsText := this.GetPhoneNumber(Setup, PhoneNumber);
    Object.Add(ObjectKey, PhoneNumberAsText);
end;
</code></pre>

<pre><code class="language-al">local procedure GetPhoneNumber(var Setup: Record Setup; PhoneNumber: Text): Text
begin
    if Setup."Application State" &lt;&gt; Enum::"Application State"::Production then
        exit(Setup."Phone Number Test");
    exit(PhoneNumber);
end;
</code></pre>

<p>Previously, I would simply have made a <code class="language-plaintext highlighter-rouge">Setup.Get()</code> at the bottom level where I needed the information.
This new approach forces me to have a well-organized architecture of my code.</p>

<h3 id="testing-in-the-context-of-a-production-environment">Testing in the context of a production environment</h3>

<pre><code class="language-al">[Test]
internal procedure TestEncodePhoneNumberProduction()
var
    TempSetup: Record Setup temporary;
    Decoders: Codeunit Decoders;
    Encoders: Codeunit Encoders;
    Object: JsonObject;
    CalculatedPhoneNumber: Text;
    CustomerPhoneNumber: Text;
begin
    // [SCENARIO #015] Testing encoding a phone number to a JSON object in a production environment.
    // [GIVEN] A phone number
    // [WHEN] encoded to JSON object
    // [THEN] we should get the same phone number

    this.TestHelper.InitializeSetup(TempSetup);
    TempSetup."Application State" := Enum::"Application State"::Production;
    CustomerPhoneNumber := '1234567890';
    Encoders.EncodePhoneNumber(TempSetup, 'Test', CustomerPhoneNumber, Object);
    Decoders.DecodeText(Object, 'Test', CalculatedPhoneNumber);
    this.Assert.AreEqual(CustomerPhoneNumber, CalculatedPhoneNumber, 'In the production environment we expected to encode the customer phone number directly.');
end;
</code></pre>

<h3 id="testing-in-the-context-of-a-test-environment">Testing in the context of a test environment</h3>

<pre><code class="language-al">[Test]
internal procedure TestEncodePhoneNumberTest()
var
    TempSetup: Record Setup temporary;
    Decoders: Codeunit Decoders;
    Encoders: Codeunit Encoders;
    Object: JsonObject;
    CalculatedPhoneNumber: Text;
    CustomerPhoneNumber: Text;
begin
    // [SCENARIO #016] Testing encoding a phone number to a JSON object in a test environment.
    // [GIVEN] A phone number
    // [WHEN] encoded to JSON object
    // [THEN] we should get the test phone number

    this.TestHelper.InitializeSetup(TempSetup);
    TempSetup."Application State" := Enum::"Application State"::Test;
    TempSetup."Phone Number Test" := '1234567890';
    CustomerPhoneNumber := '0987654321';
    Encoders.EncodePhoneNumber(TempSetup, 'Test', CustomerPhoneNumber, Object);
    Decoders.DecodeText(Object, 'Test', CalculatedPhoneNumber);
    this.Assert.AreEqual(TempSetup."Phone Number Test", CalculatedPhoneNumber, 'In the test environment we expected to ignore the customer phone number and encode the test phone number from the setup.');
end;
</code></pre>

<h3 id="running-the-actual-code">Running the actual code</h3>

<p>In the actual code, the <code class="language-plaintext highlighter-rouge">Setup.Get()</code> is executed once at the highest level. This also gives me a better understanding of all dependencies on the setup table in the code. That is, we do not retrieve the setup table in multiple places throughout the code. We get it once and pass it around by reference.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This new design has several advantages:</p>

<ul>
  <li><strong>Better performance:</strong> Fewer reads to the database.</li>
  <li><strong>Fast, isolated tests:</strong> Run tests without database access using just temporary tables.</li>
  <li><strong>Improved code overview:</strong> Dependencies are clear and easy to manage.</li>
</ul>

<p>Overall, this approach leads to a better design, even though it is not entirely how we used to write code. It is essential for writing modern, maintainable, and high-performance AL test code.</p>

<h2 id="clean-al-code-initiative">Clean AL Code Initiative</h2>

<ul>
  <li>Part 1: <a href="/programming/2023/10/10/BC-Rulesets.html">Rulesets in Business Central</a></li>
  <li>Part 2: <a href="/programming/2023/12/28/Namespaces-in-AL.html">Namespaces in AL</a></li>
  <li>Part 3: <a href="/programming/2023/12/29/Extensions-for-AL.html">VS Code Extensions for AL</a></li>
  <li>Part 4: <a href="/programming/2024/01/03/Automated-Tests-in-AL.html">Automated Tests in AL</a></li>
  <li>Part 5: <a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced CodeCop Analyzer and Custom Rulesets</a></li>
  <li>Part 6: <a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
  <li>Part 7: <a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
  <li>Part 8: <a href="/programming/2026/07/22/there-is-a-new-sheriff-in-town.html">There is a new sheriff in town</a></li>
</ul>

<h2 id="super-fast-tests-covering-100-of-your-code">Super fast tests covering 100% of your code</h2>

<ul>
  <li>Part 1: <a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces - Part 1 - System Environment</a></li>
  <li>Part 2: <a href="/programming/2025/06/26/environment-interface-part-2.html">Environment Interfaces - Part 2 - External API</a></li>
  <li>Part 3: <a href="/programming/2025/06/26/environment-interface-part-3.html">Environment Interfaces - Part 3 - Standard Application</a></li>
  <li><strong>Part 4</strong>: <a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a></li>
  <li>YouTube Video: <a href="https://areopa.academy/super-fast-tests-100-percent-code-coverage/">Super fast tests covering 100% of your code</a></li>
</ul>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[Temporary Tables in Tests]]></summary></entry><entry><title type="html">Environment Interfaces - Part 3 (Business Central)</title><link href="https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-3.html" rel="alternate" type="text/html" title="Environment Interfaces - Part 3 (Business Central)" /><published>2025-06-26T09:00:00+02:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-3</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-3.html"><![CDATA[<h2 id="environment-interfaces---part-3">Environment Interfaces - Part 3</h2>

<p>The theme for this series is <em>Super fast tests covering 100% of your code</em>.</p>

<p>This 2025 series of articles is a continuation of the 2024 series called the Clean Code Initiative.</p>

<p>Clean Code is a condition for writing good tests, but it does not guarantee code coverage by tests.</p>

<h2 id="introduction">Introduction</h2>

<p>I will introduce three different contexts of Environment Interfaces. Discussed in parts 1, 2, and 3.</p>

<ol>
  <li><a href="/programming/2025/06/26/environment-interface-part-1.html">Interface against the system environment (Sandbox or Production)</a></li>
  <li><a href="/programming/2025/06/26/environment-interface-part-2.html">Interface against an external API</a></li>
  <li>Interface against the standard application</li>
</ol>

<blockquote>
  <p><strong>Environment interfaces</strong> usually have a single implementation within the application but can have several implementations in the test application. There is no corresponding <code class="language-plaintext highlighter-rouge">Enum</code> implementing the <code class="language-plaintext highlighter-rouge">Interface</code>.</p>
</blockquote>

<h2 id="scenario">Scenario</h2>

<p>The app we are building communicates with an external API.</p>

<p>We face several key requirements:</p>

<ul>
  <li>The app can only be in production mode when running in a production environment. If running in a sandbox, it must operate in test mode.</li>
  <li>Our tests must cover all code paths, including those only reachable in production mode.</li>
  <li>Our tests cannot call the actual API, but we still need to verify both the requests we send and how we decode the API responses.</li>
  <li>Tests must be extremely fast, with runtimes measured in milliseconds.</li>
  <li>Writing tests should be quick and cost-effective.</li>
  <li>We want to test only our own code, assuming that dependencies and external systems work as expected. We assume everyone else knows what they are doing</li>
</ul>

<blockquote>
  <p><strong>Environment Interfaces</strong> are the solution to this apparent paradox.</p>
</blockquote>

<h2 id="interface-against-standard-business-central">Interface against Standard Business Central</h2>

<p>In parts 1 and 2, we have seen how interfaces enable us to reach code we could otherwise never reach in tests.</p>

<p>However, there are other important constraints: we cannot spend too much time writing tests, and we only want to test our own code.</p>

<p>This is why we sometimes have to develop an interface against the standard application.</p>

<h3 id="finance-interface">Finance interface</h3>

<p>In this case, we are handling payments and refunds.</p>

<pre><code class="language-al">namespace MicrosoftPartner.AppName.Finance;

using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Sales.History;

interface IFinance
{
    procedure CreatePaymentLine(
        var PaymentRequest: Record "Payment Request";
        var Setup: Record Setup;
        var GenJournalBatch: Record "Gen. Journal Batch";
        var GenJournalLine: Record "Gen. Journal Line";
        var GenJournalTemplate: Record "Gen. Journal Template";
        var PostedSalesInvoiceHeader: Record "Sales Invoice Header")

    procedure CreateRefundLine(
        var RefundRequest: Record "Refund Request";
        var Setup: Record Setup;
        var GenJournalBatch: Record "Gen. Journal Batch";
        var GenJournalLine: Record "Gen. Journal Line";
        var GenJournalTemplate: Record "Gen. Journal Template";
        var SalesCrMemoHeader: Record "Sales Cr.Memo Header")
}
</code></pre>

<h3 id="implementing-the-app-version-of-the-interface">Implementing the App version of the interface</h3>

<p>The implementation is as you would expect. It is the code you would normally have written.
We are inserting a GenJournalLine and we are using <code class="language-plaintext highlighter-rouge">.Validate</code> to ensure that the line is created with all the logic on the fields</p>

<pre><code class="language-al">codeunit 50019 "App Finance" implements IFinance
{
    procedure CreatePaymentLine(...)
    begin
        GenJournalLine.Init();
        GenJournalLine.Validate("Journal Template Name", GenJournalTemplate.Name);
        GenJournalLine.Validate("Journal Batch Name", GenJournalBatch.Name);
        GenJournalLine.Validate("Document Type", Enum::"Gen. Journal Document Type"::Payment);
        GenJournalLine.Validate("Document No.", PaymentRequest."Request Reference");
        
        ...
        GenJournalLine.Insert(true);
    end;

    procedure CreateRefundLine(...)
    begin
        GenJournalLine.Init();
        ...
        GenJournalLine.Insert(true);
    end;
}
</code></pre>

<h3 id="implementing-the-test-app-version-of-the-interface">Implementing the Test App version of the interface</h3>

<p>This is where it becomes interesting.</p>

<h4 id="stub-finance">Stub Finance</h4>

<p>The stub function simply returns what I have asked it to return.
This is how I control which part of my code is touched.</p>

<pre><code class="language-al">namespace MicrosoftPartner.AppName.Test.Helpers;

using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Sales.History;

codeunit 50120 "Stub Finance" implements IFinance
{
    Description = 'Stub Finance';

    var
        SetupVar: Record Setup;
        GenJournalBatchVar: Record "Gen. Journal Batch";
        GenJournalLineVar: Record "Gen. Journal Line";
        GenJournalTemplateVar: Record "Gen. Journal Template";
        PaymentRequestVar: Record "Payment Request";
        RefundRequestVar: Record "Refund Request";
        PostedSalesCrMemoHeaderVar: Record "Sales Cr.Memo Header";
        PostedSalesInvoiceHeaderVar: Record "Sales Invoice Header";

    internal procedure CreatePaymentLine(var PaymentRequest: Record "Payment Request"; var Setup: Record Setup; var GenJournalBatch: Record "Gen. Journal Batch"; var GenJournalLine: Record "Gen. Journal Line"; var GenJournalTemplate: Record "Gen. Journal Template"; var PostedSalesInvoiceHeader: Record "Sales Invoice Header")
    begin
        PaymentRequest := this.PaymentRequestVar;
        Setup := this.SetupVar;
        GenJournalBatch := this.GenJournalBatchVar;
        GenJournalLine := this.GenJournalLineVar;
        GenJournalTemplate := this.GenJournalTemplateVar;
        PostedSalesInvoiceHeader := this.PostedSalesInvoiceHeaderVar;
    end;

    internal procedure SetupPaymentResponse(var PaymentRequest: Record "Payment Request"; var Setup: Record Setup; var GenJournalBatch: Record "Gen. Journal Batch"; var GenJournalLine: Record "Gen. Journal Line"; var GenJournalTemplate: Record "Gen. Journal Template"; var PostedSalesInvoiceHeader: Record "Sales Invoice Header")
    begin
        this.PaymentRequestVar := PaymentRequest;
        this.SetupVar := Setup;
        this.GenJournalBatchVar := GenJournalBatch;
        this.GenJournalLineVar := GenJournalLine;
        this.GenJournalTemplateVar := GenJournalTemplate;
        this.PostedSalesInvoiceHeaderVar := PostedSalesInvoiceHeader;
    end;

    internal procedure CreateRefundLine(var RefundRequest: Record "Refund Request"; var Setup: Record Setup; var GenJournalBatch: Record "Gen. Journal Batch"; var GenJournalLine: Record "Gen. Journal Line"; var GenJournalTemplate: Record "Gen. Journal Template"; var SalesCrMemoHeader: Record "Sales Cr.Memo Header")
    begin
        RefundRequest := this.RefundRequestVar;
        Setup := this.SetupVar;
        GenJournalBatch := this.GenJournalBatchVar;
        GenJournalLine := this.GenJournalLineVar;
        GenJournalTemplate := this.GenJournalTemplateVar;
        SalesCrMemoHeader := this.PostedSalesCrMemoHeaderVar;
    end;

    internal procedure SetupRefundResponse(var RefundRequest: Record "Refund Request"; var Setup: Record Setup; var GenJournalBatch: Record "Gen. Journal Batch"; var GenJournalLine: Record "Gen. Journal Line"; var GenJournalTemplate: Record "Gen. Journal Template"; var SalesCrMemoHeader: Record "Sales Cr.Memo Header")
    begin
        this.RefundRequestVar := RefundRequest;
        this.SetupVar := Setup;
        this.GenJournalBatchVar := GenJournalBatch;
        this.GenJournalLineVar := GenJournalLine;
        this.GenJournalTemplateVar := GenJournalTemplate;
        this.PostedSalesCrMemoHeaderVar := SalesCrMemoHeader;
    end;
}
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Running logic in the base application introduces several challenges:</p>

<p>Setting up tests is time-consuming due to dependencies on G/L Accounts, Banks, General Journal Batch, General Journal Template, Number Series, and more — a rabbit hole that can consume significant amounts of time.</p>

<p>It creates a strong dependency on the base app, increasing maintenance overhead.</p>

<p>It is slow, as it writes to the database and rolls back changes, preventing the use of temporary variables.</p>

<p>By using Environment Interfaces, I have avoided these issues. My tests are fast, independent, and focused on my code.</p>

<p>You might say that this is cheating. That I am not really testing the code.
And you are right in the sense that I have to understand perfectly what Business Central is returning when it creates the General Journal Line. But I am not testing that Business Central is working; I am testing that my code is working.</p>

<h2 id="clean-al-code-initiative">Clean AL Code Initiative</h2>

<ul>
  <li>Part 1: <a href="/programming/2023/10/10/BC-Rulesets.html">Rulesets in Business Central</a></li>
  <li>Part 2: <a href="/programming/2023/12/28/Namespaces-in-AL.html">Namespaces in AL</a></li>
  <li>Part 3: <a href="/programming/2023/12/29/Extensions-for-AL.html">VS Code Extensions for AL</a></li>
  <li>Part 4: <a href="/programming/2024/01/03/Automated-Tests-in-AL.html">Automated Tests in AL</a></li>
  <li>Part 5: <a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced CodeCop Analyzer and Custom Rulesets</a></li>
  <li>Part 6: <a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
  <li>Part 7: <a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
  <li>Part 8: <a href="/programming/2026/07/22/there-is-a-new-sheriff-in-town.html">There is a new sheriff in town</a></li>
</ul>

<h2 id="super-fast-tests-covering-100-of-your-code">Super fast tests covering 100% of your code</h2>

<ul>
  <li>Part 1: <a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces - Part 1 - System Environment</a></li>
  <li>Part 2: <a href="/programming/2025/06/26/environment-interface-part-2.html">Environment Interfaces - Part 2 - External API</a></li>
  <li><strong>Part 3</strong>: <a href="/programming/2025/06/26/environment-interface-part-3.html">Environment Interfaces - Part 3 - Standard Application</a></li>
  <li>Part 4: <a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a></li>
  <li>YouTube Video: <a href="https://areopa.academy/super-fast-tests-100-percent-code-coverage/">Super fast tests covering 100% of your code</a></li>
</ul>

<h2 id="london-style-terminology">London Style Terminology</h2>

<p>When writing tests in Business Central, you may come across different types of test doubles—objects that replace real dependencies in your code. The “London style” (or “mockist”) terminology is widely used in the testing community to describe these patterns. Understanding these terms will help you communicate more clearly about your test design and choose the right approach for your scenario.</p>

<table>
  <thead>
    <tr>
      <th>Term</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Dummy</strong></td>
      <td>An object passed as a parameter but never used. Only exists to fill parameter lists.</td>
    </tr>
    <tr>
      <td><strong>Stub</strong></td>
      <td>A fake object that returns fixed data and <strong>does not track usage</strong>.</td>
    </tr>
    <tr>
      <td><strong>Fake</strong></td>
      <td>A working implementation with simplified behavior (e.g., in-memory database).</td>
    </tr>
    <tr>
      <td><strong>Spy</strong></td>
      <td>Like a stub, but records how it was called for later verification.</td>
    </tr>
    <tr>
      <td><strong>Mock</strong></td>
      <td>A fake that <strong>also verifies</strong> how it was used (e.g., was method X called?).</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[Environment Interfaces - Part 3]]></summary></entry><entry><title type="html">Environment Interfaces - Part 2 (Business Central)</title><link href="https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-2.html" rel="alternate" type="text/html" title="Environment Interfaces - Part 2 (Business Central)" /><published>2025-06-26T08:00:00+02:00</published><updated>2026-07-22T07:00:00+02:00</updated><id>https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-2</id><content type="html" xml:base="https://www.finnpedersenfrance.com/programming/2025/06/26/environment-interface-part-2.html"><![CDATA[<h2 id="environment-interfaces---part-2">Environment Interfaces - Part 2</h2>

<p>The theme for this series is <em>Super fast tests covering 100% of your code</em>.</p>

<p>This 2025 series of articles is a continuation of the 2024 series called the Clean Code Initiative.</p>

<p>Clean Code is a condition for writing good tests, but it does not guarantee code coverage by tests.</p>

<h2 id="introduction">Introduction</h2>

<p>I will introduce three different contexts of Environment Interfaces. Discussed in parts 1, 2, and 3.</p>

<ol>
  <li><a href="/programming/2025/06/26/environment-interface-part-1.html">Interface against the system environment (Sandbox or Production)</a></li>
  <li>Interface against an external API</li>
  <li><a href="/programming/2025/06/26/environment-interface-part-3.html">Interface against the standard application</a></li>
</ol>

<blockquote>
  <p><strong>Environment interfaces</strong> usually have a single implementation within the application but can have several implementations in the test application. There is no corresponding <code class="language-plaintext highlighter-rouge">Enum</code> implementing the <code class="language-plaintext highlighter-rouge">Interface</code>.</p>
</blockquote>

<h2 id="scenario">Scenario</h2>

<p>The app we are building communicates with an external API.</p>

<p>We face several key requirements:</p>

<ul>
  <li>The app can only be in production mode when running in a production environment. If running in a sandbox, it must operate in test mode.</li>
  <li>Our tests must cover all code paths, including those only reachable in production mode.</li>
  <li>Our tests cannot call the actual API, but we still need to verify both the requests we send and how we decode the API responses.</li>
  <li>Tests must be extremely fast, with runtimes measured in milliseconds.</li>
  <li>Writing tests should be quick and cost-effective.</li>
  <li>We want to test only our own code, assuming that dependencies and external systems work as expected. We assume everyone else knows what they are doing</li>
</ul>

<blockquote>
  <p><strong>Environment Interfaces</strong> are the solution to this apparent paradox.</p>
</blockquote>

<h2 id="interface-against-the-external-api">Interface against the external API</h2>

<p>Before describing the interface, I need a small helper Enum.</p>

<h3 id="api-method-enum">Api Method Enum</h3>

<p>There is a standard Enum, but I just want to keep it simple and to what I need.</p>

<pre><code class="language-al">enum 50005 "Api Method"
{
    value(0; GET)
    {
        Caption = 'GET', Locked = true;
    }
    value(1; POST)
    {
        Caption = 'POST', Locked = true;
    }
}
</code></pre>

<h3 id="api-request-interface">Api Request Interface</h3>

<p>It is a very simple interface with just one function <strong>Send</strong>.
But it is all we need.</p>

<pre><code class="language-al">interface IApiRequest
{
    procedure Send(RequestMethod: Enum "Api Method"; RequestUrl: Text; Payload: Text; SecretKey: SecretText; var ResponseStatusCode: Integer; var ResponseContent: Text)
}
</code></pre>

<h3 id="implementing-the-app-version-of-the-interface">Implementing the App version of the interface</h3>

<p>The implementation of the Send function is quite generic and you can reuse this code. The API accepts a JSON payload, an encrypted key, and it returns a Status Code (200 for OK) and a response payload also in JSON.</p>

<pre><code class="language-al">codeunit 50011 "App Api Request" implements IApiRequest
{
    internal procedure Send(RequestMethod: Enum "Api Method"; RequestUrl: Text; Payload: Text; SecretKey: SecretText; var ResponseStatusCode: Integer; var ResponseContent: Text)
    var
        ApiTools: Codeunit ApiTools;
        HttpClient: HttpClient;
        HttpContent: HttpContent;
        ContentHeaders: HttpHeaders;
        RequestHeaders: HttpHeaders;
        HttpRequestMessage: HttpRequestMessage;
        HttpResponseMessage: HttpResponseMessage;
        Hash: SecretText;
    begin
        Hash := ApiTools.CreateHash(Payload, SecretKey);

        HttpRequestMessage.Method := Format(RequestMethod);
        HttpRequestMessage.SetRequestUri(RequestUrl);
        HttpRequestMessage.GetHeaders(RequestHeaders);
        RequestHeaders.Clear();
        RequestHeaders.Add('Accept', 'application/json');
        RequestHeaders.Add('Authorization', Hash);

        if RequestMethod = Enum::"Api Method"::POST then begin
            HttpContent.WriteFrom(Payload);
            HttpContent.GetHeaders(ContentHeaders);
            ContentHeaders.Clear();
            ContentHeaders.Add('Content-Type', 'application/json');
            HttpRequestMessage.Content := HttpContent;
        end;

        if HttpClient.Send(HttpRequestMessage, HttpResponseMessage) then begin
            ResponseStatusCode := HttpResponseMessage.HttpStatusCode();
            if ResponseStatusCode = ApiTools.HttpStatusCodeOK() then
                HttpResponseMessage.Content().ReadAs(ResponseContent)
            else
                ApiTools.WebServiceCallFailedError(HttpResponseMessage.HttpStatusCode());
        end else
            ApiTools.ConnectionError();
    end;
}
</code></pre>

<h3 id="implementing-the-test-app-version-of-the-interface">Implementing the Test App version of the interface</h3>

<p>This is where it becomes interesting.</p>

<h4 id="stub-api-request">Stub Api Request</h4>

<p>The stub function simply returns what I have asked it to return.
This is how I control which part of my code is touched.</p>

<pre><code class="language-al">codeunit 50113 "Stub Api Request" implements IApiRequest
{
    Description = 'Stub Api Request';

    var
        ResponseStatusCodeVar: Integer;
        ResponseContentVar: Text;

    internal procedure Send(RequestMethod: Enum "Api Method"; RequestUrl: Text; Payload: Text; SecretKey: SecretText; var ResponseStatusCode: Integer; var ResponseContent: Text)
    begin
        ResponseStatusCode := this.ResponseStatusCodeVar;
        ResponseContent := this.ResponseContentVar;
    end;

    internal procedure SetupResponse(ResponseStatusCode: Integer; ResponseContent: Text)
    begin
        this.ResponseStatusCodeVar := ResponseStatusCode;
        this.ResponseContentVar := ResponseContent;
    end;
}
</code></pre>

<h3 id="using-it">Using it</h3>

<p>For each endpoint the API exposes, I have written a Codeunit with these functions</p>

<pre><code class="language-al">codeunit 50007 "Api Operation &lt;Endpointname&gt;"
{
    Description = 'Implementation of the Api Operation &lt;Endpointname&gt;';

    internal procedure OperationType(): Text
    begin
        exit('&lt;Endpointname&gt;');
    end;

    internal procedure RequestMethod(): Enum "Api Method"
    begin
        exit(Enum::"Api Method"::POST);
    end;

    internal procedure RequestPayload(var EndpointTable: Record "Endpoint Table Name"): Text[2048]
    begin
        exit(&lt;the calculated payload&gt;);
    end;

    internal procedure SendRequest(var Setup: Record Setup"; var EndpointTable: Record "Endpoint Table Name"; ApiRequest: Interface IApiRequest)
    var
        Decoders: Codeunit Decoders;
        ResponseStatusCode: Integer;
        SecretKey: SecretText;
        RequestUrl: Text;
        ResponseContent: Text;
    begin
        RequestUrl := Setup.ApiUrl(this.OperationType());
        SecretKey := Setup.ApiHmac();
        ResponseStatusCode := 0;
        ApiRequest.Send(this.RequestMethod(), RequestUrl, this.RequestPayload(EndpointTable), SecretKey, ResponseStatusCode, ResponseContent);
        Decoders.DecodeEndpointResponse(Setup, ResponseStatusCode, ResponseContent, EndpointTable);
    end;
}
</code></pre>

<h4 id="running-the-actual-code">Running the actual code</h4>

<p>When I call the SendRequest function, I pass it the implementation.</p>

<pre><code class="language-al">var
  AppApiRequest: Codeunit "App Api Request";
...

ApiOperationEndpoint.SendRequest(Setup, Rec, AppApiRequest);
</code></pre>

<h4 id="testing-in-the-context-of-a-fake-api">Testing in the context of a fake API</h4>

<p>Before calling <code class="language-plaintext highlighter-rouge">SendRequest</code>, I call the <code class="language-plaintext highlighter-rouge">SetupResponse</code> and tell my fake API what to return to me.
I know that this sounds strange at first, but trust me. I am not testing if the API works.
I am testing how my code reacts to the response from the API.
In this way, I can ensure that I am also testing more exotic events and responses.</p>

<p><strong>This is the setup for success:</strong></p>

<pre><code class="language-al">StubApiRequest.SetupResponse(200, '{"Result":"OK","Reference":"TEST"}');
ApiOperationEndpoint.SendRequest(TempSetup, TempRecord, StubApiRequest);
</code></pre>

<p><strong>Failure could look like this:</strong></p>

<pre><code class="language-al">StubApiRequest.SetupResponse(200, StubApiResponses.NotUniqueErrorResponse());
ApiOperationEndpoint.SendRequest(TempSetup, TempRecord, StubApiRequest);
</code></pre>

<p>I still get a valid JSON, but something is wrong. Then I am testing if my decoders are working as they should.</p>

<h2 id="conclusion">Conclusion</h2>

<p>By using stubs and environment interfaces, I can thoroughly test all my decoding logic, failure detection, and error handling — without ever calling the real API during tests.</p>

<p>This approach ensures that every line of my code is exercised and verified, resulting in fast, reliable, and maintainable tests for my Business Central app.</p>

<h2 id="clean-al-code-initiative">Clean AL Code Initiative</h2>

<ul>
  <li>Part 1: <a href="/programming/2023/10/10/BC-Rulesets.html">Rulesets in Business Central</a></li>
  <li>Part 2: <a href="/programming/2023/12/28/Namespaces-in-AL.html">Namespaces in AL</a></li>
  <li>Part 3: <a href="/programming/2023/12/29/Extensions-for-AL.html">VS Code Extensions for AL</a></li>
  <li>Part 4: <a href="/programming/2024/01/03/Automated-Tests-in-AL.html">Automated Tests in AL</a></li>
  <li>Part 5: <a href="/programming/2024/01/05/BC-Advanced-Rulesets.html">Advanced CodeCop Analyzer and Custom Rulesets</a></li>
  <li>Part 6: <a href="/programming/2024/01/11/how-to-make-a-code-review.html">How to make a code review</a></li>
  <li>Part 7: <a href="/programming/2024/01/12/TryFunctions-in-AL.html">Preconditions and TryFunctions in AL</a></li>
  <li>Part 8: <a href="/programming/2026/07/22/there-is-a-new-sheriff-in-town.html">There is a new sheriff in town</a></li>
</ul>

<h2 id="super-fast-tests-covering-100-of-your-code">Super fast tests covering 100% of your code</h2>

<ul>
  <li>Part 1: <a href="/programming/2025/06/26/environment-interface-part-1.html">Environment Interfaces - Part 1 - System Environment</a></li>
  <li><strong>Part 2</strong>: <a href="/programming/2025/06/26/environment-interface-part-2.html">Environment Interfaces - Part 2 - External API</a></li>
  <li>Part 3: <a href="/programming/2025/06/26/environment-interface-part-3.html">Environment Interfaces - Part 3 - Standard Application</a></li>
  <li>Part 4: <a href="/programming/2025/06/26/temporary-tables-in-tests.html">Temporary Tables in Tests</a></li>
  <li>YouTube Video: <a href="https://areopa.academy/super-fast-tests-100-percent-code-coverage/">Super fast tests covering 100% of your code</a></li>
</ul>

<h2 id="london-style-terminology">London Style Terminology</h2>

<p>When writing tests in Business Central, you may come across different types of test doubles—objects that replace real dependencies in your code. The “London style” (or “mockist”) terminology is widely used in the testing community to describe these patterns. Understanding these terms will help you communicate more clearly about your test design and choose the right approach for your scenario.</p>

<table>
  <thead>
    <tr>
      <th>Term</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Dummy</strong></td>
      <td>An object passed as a parameter but never used. Only exists to fill parameter lists.</td>
    </tr>
    <tr>
      <td><strong>Stub</strong></td>
      <td>A fake object that returns fixed data and <strong>does not track usage</strong>.</td>
    </tr>
    <tr>
      <td><strong>Fake</strong></td>
      <td>A working implementation with simplified behavior (e.g., in-memory database).</td>
    </tr>
    <tr>
      <td><strong>Spy</strong></td>
      <td>Like a stub, but records how it was called for later verification.</td>
    </tr>
    <tr>
      <td><strong>Mock</strong></td>
      <td>A fake that <strong>also verifies</strong> how it was used (e.g., was method X called?).</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Finn</name></author><category term="Programming" /><summary type="html"><![CDATA[Environment Interfaces - Part 2]]></summary></entry></feed>