<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: khalil la</title>
    <description>The latest articles on DEV Community by khalil la (@gridou).</description>
    <link>https://dev.to/gridou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F233548%2F60b877e6-11fa-4923-9581-6f2876a9be3b.png</url>
      <title>DEV Community: khalil la</title>
      <link>https://dev.to/gridou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gridou"/>
    <language>en</language>
    <item>
      <title>Meet Semantic Components — A Modern Angular UI Library</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Wed, 25 Feb 2026 19:26:58 +0000</pubDate>
      <link>https://dev.to/gridou/meet-semantic-components-a-modern-angular-ui-library-3352</link>
      <guid>https://dev.to/gridou/meet-semantic-components-a-modern-angular-ui-library-3352</guid>
      <description>&lt;p&gt;After waiting so long for an Angular UI library that actually met my needs, I decided to stop waiting and build my own. The result is &lt;strong&gt;Semantic Components&lt;/strong&gt; — an open-source Angular UI library built on Tailwind CSS, Angular CDK, and Angular Aria, heavily inspired by &lt;a href="https://ui.shadcn.com" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/gridatek/semantic-components" rel="noopener noreferrer"&gt;https://github.com/gridatek/semantic-components&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Package:&lt;/strong&gt; &lt;code&gt;@semantic-components/ui&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://semantic-components.com" rel="noopener noreferrer"&gt;https://semantic-components.com&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Semantic Components?
&lt;/h2&gt;

&lt;p&gt;The Angular ecosystem has always had fewer off-the-shelf UI options compared to React. Libraries like shadcn/ui, Radix, and Headless UI have raised the bar for what a component library can be — and Angular deserves the same quality.&lt;/p&gt;

&lt;p&gt;Semantic Components is my attempt to bring that standard to Angular, while leaning fully into what makes Angular great.&lt;/p&gt;


&lt;h2&gt;
  
  
  Core Design Principles
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Semantic
&lt;/h3&gt;

&lt;p&gt;Every directive or component is named to describe its &lt;strong&gt;role&lt;/strong&gt; in the interface, not just the feature it belongs to. Take the tooltip as an example. Angular Material gives you a single &lt;code&gt;matTooltip&lt;/code&gt; directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Angular Material --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;matTooltip=&lt;/span&gt;&lt;span class="s"&gt;"Save changes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Semantic Components, it's scTooltipTrigger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Semantic Components --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scTooltipTrigger=&lt;/span&gt;&lt;span class="s"&gt;"Save changes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;scTooltipTrigger&lt;/code&gt; — because &lt;code&gt;ScTooltip&lt;/code&gt; is already the component that &lt;em&gt;renders&lt;/em&gt; the actual tooltip bubble. The directive on the button is not the tooltip — it's what triggers it. These are two different things, and the names reflect that. &lt;code&gt;ScDrawerTrigger&lt;/code&gt;, &lt;code&gt;ScSelectValue&lt;/code&gt;, &lt;code&gt;ScSelectTrigger&lt;/code&gt;, &lt;code&gt;ScSidebarBody&lt;/code&gt; — you know exactly what each piece does before reading a single line of docs.&lt;/p&gt;

&lt;p&gt;This principle extends to the &lt;strong&gt;HTML elements themselves&lt;/strong&gt;. When possible, components/directives are applied to the right native element rather than a generic &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative
&lt;/h3&gt;

&lt;p&gt;The entire UI is described in the template — no imperative &lt;code&gt;open()&lt;/code&gt;, &lt;code&gt;close()&lt;/code&gt;, or &lt;code&gt;DialogService.create()&lt;/code&gt; calls. Take the dialog as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scDialogProvider&lt;/span&gt; &lt;span class="na"&gt;[(open)]=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scDialogTrigger&lt;/span&gt; &lt;span class="na"&gt;scButton&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"outline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open Dialog&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;scDialogPortal&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scDialog&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scDialogClose&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;siXIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scDialogHeader&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;scDialogTitle&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Edit profile&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;scDialogDescription&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Make changes to your profile here.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- content --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scDialogFooter&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scButton&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"outline"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"isOpen.set(false)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scButton&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save changes&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The open state is a signal. The trigger, the portal, the close button — all declared in the template. No service injection, no imperative show/hide, no &lt;code&gt;ViewContainerRef&lt;/code&gt; gymnastics. You read the template and immediately understand the full structure of the dialog.&lt;/p&gt;

&lt;p&gt;The naming reinforces this. &lt;code&gt;ScDialog&lt;/code&gt; is not a service — it's the &lt;code&gt;&amp;lt;div role="dialog"&amp;gt;&lt;/code&gt; element itself. In Angular Material, &lt;code&gt;MatDialog&lt;/code&gt; is a service you inject and call &lt;code&gt;.open()&lt;/code&gt; on. Here, &lt;code&gt;scDialog&lt;/code&gt; is the thing rendered in the DOM. Same naming principle: the name describes exactly what the piece &lt;em&gt;is&lt;/em&gt;, not what it &lt;em&gt;does behind the scenes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There is a tradeoff: &lt;code&gt;scDialog&lt;/code&gt; requires an extra wrapper element in the DOM &lt;code&gt;scDialogProvider&lt;/code&gt;. It acts as the coordination point between the trigger, the portal, and the close button — sharing state through Angular's DI tree. It's a conscious choice in favor of keeping everything in the template, at the cost of one extra &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that you may need to style or account for in your layout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composable
&lt;/h3&gt;

&lt;p&gt;Each component is a set of small, focused pieces that you assemble yourself. There are no magic &lt;code&gt;[content]&lt;/code&gt; inputs or hidden &lt;code&gt;&amp;lt;ng-content&amp;gt;&lt;/code&gt; slots — you write the structure, and the pieces plug into it.&lt;/p&gt;

&lt;p&gt;The Select is a good example of how far this goes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scSelect&lt;/span&gt; &lt;span class="na"&gt;#select&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="na"&gt;scSelect&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Select a label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scSelectTrigger&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Label dropdown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;scSelectValue&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      @if (displayIcon(); as icon) {
      &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;scSelectItemIcon&lt;/span&gt; &lt;span class="na"&gt;siTagIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
      }
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"truncate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ select.displayValue() }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;scSelectPortal&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scSelectPopup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scSelectList&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        @for (item of items; track item.value) {
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;scSelectItem&lt;/span&gt; &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"item.value"&lt;/span&gt; &lt;span class="na"&gt;[label]=&lt;/span&gt;&lt;span class="s"&gt;"item.label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;scSelectItemIcon&lt;/span&gt; &lt;span class="na"&gt;siTagIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.label }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        }
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You own the structure&lt;/strong&gt; — the trigger layout, the item layout, the icons, the display value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You extend freely&lt;/strong&gt; — want a custom empty state in the list? A header above the items? Just add it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The library handles behavior&lt;/strong&gt; — keyboard navigation, selection state, ARIA attributes — you handle the markup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This also composes across components. A button can be a drawer trigger, a tooltip trigger, and an icon button all at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scButton&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;scDrawerTrigger&lt;/span&gt; &lt;span class="na"&gt;scTooltipTrigger=&lt;/span&gt;&lt;span class="s"&gt;"Open menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;siMenuIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One element. Three responsibilities. No wrappers.&lt;/p&gt;

&lt;p&gt;The tradeoff is verbosity. Because you own the structure, you write more template code than you would with a batteries-included component that hides everything behind inputs. That's a deliberate choice — explicit over implicit. You always know what's in the DOM because you put it there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailwind + CVA for Variants
&lt;/h3&gt;

&lt;p&gt;The library follows the &lt;a href="https://ui.shadcn.com" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; design system — same CSS variables, same color tokens (&lt;code&gt;bg-primary&lt;/code&gt;, &lt;code&gt;text-muted-foreground&lt;/code&gt;, &lt;code&gt;border-input&lt;/code&gt;…), same default styles. If you're already familiar with shadcn, the visual language is instantly recognizable.&lt;/p&gt;

&lt;p&gt;Styles are written in Tailwind CSS and managed with &lt;a href="https://cva.style" rel="noopener noreferrer"&gt;class-variance-authority&lt;/a&gt;. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Predictable, overridable class names&lt;/li&gt;
&lt;li&gt;Consistent variants (&lt;code&gt;default&lt;/code&gt;, &lt;code&gt;outline&lt;/code&gt;, &lt;code&gt;ghost&lt;/code&gt;, &lt;code&gt;destructive&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;) across all components
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonVariants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cva&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline-flex items-center justify-center rounded-lg border ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-primary text-primary-foreground&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-border bg-background hover:bg-muted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover:bg-muted hover:text-foreground&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;destructive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-destructive/10 text-destructive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-primary underline-offset-4 hover:underline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-8 px-2.5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-7 px-2.5 text-[0.8rem]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-9 px-2.5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;size-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Built on Solid Foundations
&lt;/h3&gt;

&lt;p&gt;The rest of the library's design is guided by a few core principles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attribute selectors over element selectors.&lt;/strong&gt; Instead of custom elements like &lt;code&gt;&amp;lt;sc-button&amp;gt;&lt;/code&gt;, the library uses attribute selectors on native HTML. No extra wrapper elements, native accessibility roles preserved, and multiple components/directives can stack on the same element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;scButton&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"outline"&lt;/span&gt; &lt;span class="na"&gt;scDrawerTrigger&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Modern Angular, all the way down.&lt;/strong&gt; Signals (&lt;code&gt;input()&lt;/code&gt;, &lt;code&gt;output()&lt;/code&gt;, &lt;code&gt;computed()&lt;/code&gt;), standalone components, native control flow (&lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@for&lt;/code&gt;), &lt;code&gt;inject()&lt;/code&gt;, and OnPush everywhere. Overlays and positioning are built on &lt;code&gt;@angular/cdk&lt;/code&gt;. Accessible patterns like focus trapping and live regions use &lt;code&gt;@angular/cdk/a11y&lt;/code&gt; and &lt;code&gt;@angular/aria&lt;/code&gt;. Forms are signal-based. The library is also zoneless-compatible — no &lt;code&gt;zone.js&lt;/code&gt; required. No legacy APIs, no NgModules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button[scButton]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScButton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ScButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ScButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;booleanAttribute&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Accessible by default.&lt;/strong&gt; Every component is built to pass WCAG AA minimums — proper ARIA attributes, full keyboard navigation, focus management on dialogs and drawers, and screen reader support. Where possible, this is powered by Angular CDK's accessibility primitives (&lt;code&gt;@angular/cdk/a11y&lt;/code&gt;) and &lt;code&gt;@angular/aria&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tradeoffs
&lt;/h2&gt;

&lt;p&gt;This library makes deliberate choices that prioritize the future of Angular over backwards compatibility. That means it is &lt;strong&gt;not for every project&lt;/strong&gt; — and that's intentional.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zoneless only.&lt;/strong&gt; The library is built for zoneless Angular apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OnPush only.&lt;/strong&gt; All components use &lt;code&gt;ChangeDetectionStrategy.OnPush&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal-based forms only.&lt;/strong&gt; Form integrations are designed around signals, not &lt;code&gt;NgModel&lt;/code&gt; or reactive forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No NgModules.&lt;/strong&gt; Everything is standalone. There are no module exports, no &lt;code&gt;forRoot()&lt;/code&gt;, no compatibility shims for module-based apps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's in the Box
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;@semantic-components/ui&lt;/code&gt; — Core Library
&lt;/h3&gt;

&lt;p&gt;50+ components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Actions&lt;/td&gt;
&lt;td&gt;Button, Button Group, Link, Toggle, Toggle Group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layout&lt;/td&gt;
&lt;td&gt;Card, Separator, Aspect Ratio, Toolbar, Scroll Area, Typography&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forms&lt;/td&gt;
&lt;td&gt;Input, Textarea, Checkbox, Radio Group, Switch, Select, Native Select, Label, Field, Input Group, Slider, Range Slider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overlays&lt;/td&gt;
&lt;td&gt;Dialog, Alert Dialog, Drawer, Sheet, Popover, Hover Card, Tooltip, Toast, Backdrop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Breadcrumb, Pagination, Tabs, Menu, Menu Bar, Navigation Menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Display&lt;/td&gt;
&lt;td&gt;Alert, Badge, Avatar, Skeleton, Spinner, Progress, Kbd, Empty, Item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;Table, Accordion, Collapsible, Calendar, Date Picker, Time Picker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;File Upload&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Icons: &lt;code&gt;@semantic-icons/lucide-icons&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Icons are distributed as Angular components from &lt;code&gt;@semantic-icons/lucide-icons&lt;/code&gt;. Every icon is a standalone component you apply to an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;siStarIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;siUserIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;siArrowRightIcon&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is fully tree-shakable — only the icons you import end up in your bundle. No icon fonts, no sprite sheets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @semantic-components/ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the styles to your global stylesheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'@semantic-components/ui/styles'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@source&lt;/span&gt; &lt;span class="s1"&gt;"../node_modules/@semantic-components/ui"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@import&lt;/code&gt; brings in the CDK overlay styles and the shadcn-compatible CSS variables (colors, radius, spacing tokens). The &lt;code&gt;@source&lt;/code&gt; tells Tailwind v4 to scan the library's files so its utility classes are included in your build.&lt;/p&gt;

&lt;p&gt;Then import what you need directly in your standalone component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ScButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialogBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialogTitle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@semantic-components/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ScButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialogBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScDialogTitle&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;button scButton&amp;gt;Open Dialog&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No module registration. No &lt;code&gt;forRoot()&lt;/code&gt;. Just import and use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/gridatek/semantic-components" rel="noopener noreferrer"&gt;https://github.com/gridatek/semantic-components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@semantic-components/ui" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@semantic-components/ui&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback, stars, and contributions are very welcome. If you're building Angular apps and tired of fighting your UI library, give Semantic Components a try.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ui</category>
      <category>tailwindcss</category>
      <category>shadcn</category>
    </item>
    <item>
      <title>JVM-Level vs Spring Boot-Level SSL Configuration</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Mon, 27 Oct 2025 11:56:15 +0000</pubDate>
      <link>https://dev.to/gridou/jvm-level-vs-spring-boot-level-ssl-configuration-d55</link>
      <guid>https://dev.to/gridou/jvm-level-vs-spring-boot-level-ssl-configuration-d55</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;When securing a Spring Boot application, SSL/TLS configuration can occur at two different layers: the &lt;strong&gt;JVM level&lt;/strong&gt; and the &lt;strong&gt;Spring Boot level&lt;/strong&gt;. Although both deal with certificates and encryption, they operate independently and serve different purposes. Understanding this distinction is essential for correctly securing traffic both into and out of your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. JVM-Level SSL (Global to the Process)
&lt;/h2&gt;

&lt;p&gt;JVM-level SSL configuration applies to the &lt;strong&gt;entire Java Virtual Machine&lt;/strong&gt;, affecting all outbound TLS communication initiated by the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Outbound HTTPS calls&lt;/li&gt;
&lt;li&gt;Trust anchors (which CAs the JVM trusts)&lt;/li&gt;
&lt;li&gt;Client certificates for outbound mutual TLS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuration example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.keyStore&lt;span class="o"&gt;=&lt;/span&gt;/etc/certs/client-keystore.p12
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.keyStorePassword&lt;span class="o"&gt;=&lt;/span&gt;changeit
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.trustStore&lt;span class="o"&gt;=&lt;/span&gt;/etc/certs/jvm-truststore.jks
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.trustStorePassword&lt;span class="o"&gt;=&lt;/span&gt;changeit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Spring Boot-Level SSL (Embedded Server)
&lt;/h2&gt;

&lt;p&gt;Spring Boot-level SSL is used by the &lt;strong&gt;embedded web server&lt;/strong&gt; (Tomcat/Jetty/Undertow). This controls the TLS endpoint your application exposes to callers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Inbound HTTPS traffic&lt;/li&gt;
&lt;li&gt;Server certificate presented to clients&lt;/li&gt;
&lt;li&gt;Optional client authentication for inbound mutual TLS (configured via &lt;code&gt;server.ssl.client-auth&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuration example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;key-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:server.p12&lt;/span&gt;
    &lt;span class="na"&gt;key-store-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeit&lt;/span&gt;
    &lt;span class="na"&gt;key-store-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PKCS12&lt;/span&gt;
    &lt;span class="na"&gt;trust-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:client-truststore.jks&lt;/span&gt;
    &lt;span class="na"&gt;trust-store-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeit&lt;/span&gt;
    &lt;span class="na"&gt;client-auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;need&lt;/span&gt;  &lt;span class="c1"&gt;# Options: none, want, need&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;trust-store&lt;/code&gt; property is only used when &lt;code&gt;client-auth&lt;/code&gt; is set to &lt;code&gt;want&lt;/code&gt; or &lt;code&gt;need&lt;/code&gt;. It validates client certificates during mutual TLS authentication.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;JVM-Level SSL&lt;/th&gt;
&lt;th&gt;Spring Boot-Level SSL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Entire JVM (default for all connections)&lt;/td&gt;
&lt;td&gt;Only Spring Boot web server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direction&lt;/td&gt;
&lt;td&gt;Outbound (and default inbound)&lt;/td&gt;
&lt;td&gt;Inbound (overrides JVM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Trust upstream services, outbound requests&lt;/td&gt;
&lt;td&gt;Serve HTTPS to callers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-Djavax.net.ssl.*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;application.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Clarification:&lt;/strong&gt; JVM-level settings provide defaults for all SSL/TLS connections, but Spring Boot's embedded server overrides these defaults with its own configuration for inbound traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. When You Need Both
&lt;/h2&gt;

&lt;p&gt;A typical microservice often acts as both &lt;strong&gt;a TLS server&lt;/strong&gt; (for inbound requests) and &lt;strong&gt;a TLS client&lt;/strong&gt; (for outbound dependencies). In this scenario, both SSL layers must be configured.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. How to Combine JVM-Level and Spring Boot-Level SSL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Combined Use Case
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What is configured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Present a server certificate to clients&lt;/td&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.ssl.key-store&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trust incoming clients for mTLS&lt;/td&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.ssl.trust-store&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trust upstream service certificates&lt;/td&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;javax.net.ssl.trustStore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Present client cert for outbound mTLS&lt;/td&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;javax.net.ssl.keyStore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Combined configuration example
&lt;/h3&gt;

&lt;h4&gt;
  
  
  JVM-level (outbound)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.keyStore&lt;span class="o"&gt;=&lt;/span&gt;/etc/certs/outbound-client-keystore.p12
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.keyStorePassword&lt;span class="o"&gt;=&lt;/span&gt;changeit
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.trustStore&lt;span class="o"&gt;=&lt;/span&gt;/etc/certs/outbound-truststore.jks
&lt;span class="nt"&gt;-Djavax&lt;/span&gt;.net.ssl.trustStorePassword&lt;span class="o"&gt;=&lt;/span&gt;changeit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Spring Boot-level (inbound)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;key-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:inbound-server-keystore.p12&lt;/span&gt;
    &lt;span class="na"&gt;key-store-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeit&lt;/span&gt;
    &lt;span class="na"&gt;trust-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:inbound-truststore.jks&lt;/span&gt;
    &lt;span class="na"&gt;trust-store-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeit&lt;/span&gt;
    &lt;span class="na"&gt;client-auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;need&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to keep them separate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Different certificate authorities&lt;/li&gt;
&lt;li&gt;Different rotation lifecycles&lt;/li&gt;
&lt;li&gt;Security boundary separation&lt;/li&gt;
&lt;li&gt;mTLS with asymmetric trust domains&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When they can be merged
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Single CA for both directions&lt;/li&gt;
&lt;li&gt;Small trust domain&lt;/li&gt;
&lt;li&gt;Simplified deployment model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Advanced Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTTP Client Libraries
&lt;/h3&gt;

&lt;p&gt;Some HTTP client libraries (RestTemplate, WebClient, Apache HttpClient, etc.) may allow or require custom SSL context configuration beyond JVM-level settings. This is useful when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different trust stores for different downstream services&lt;/li&gt;
&lt;li&gt;Per-client SSL configurations&lt;/li&gt;
&lt;li&gt;Programmatic certificate validation logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example with RestTemplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SSLContext&lt;/span&gt; &lt;span class="n"&gt;sslContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SSLContextBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadTrustMaterial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trustStore&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;SSLConnectionSocketFactory&lt;/span&gt; &lt;span class="n"&gt;socketFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SSLConnectionSocketFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sslContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClients&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSSLSocketFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socketFactory&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;RestTemplate&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestTemplate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpComponentsClientHttpRequestFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traffic Direction&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inbound (clients → you)&lt;/td&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;application.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound (you → others)&lt;/td&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;JVM &lt;code&gt;-D&lt;/code&gt; flags&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both layers serve different purposes but can work together when an application is both a TLS server and a TLS client.&lt;/p&gt;




</description>
      <category>springboot</category>
      <category>jvm</category>
      <category>ssl</category>
    </item>
    <item>
      <title>Announcing `tw-prose`: A CSS-Only Typography Plugin for Tailwind CSS v4</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Wed, 10 Sep 2025 21:28:55 +0000</pubDate>
      <link>https://dev.to/gridou/announcing-tw-prose-a-css-only-typography-plugin-for-tailwind-css-v4-o8j</link>
      <guid>https://dev.to/gridou/announcing-tw-prose-a-css-only-typography-plugin-for-tailwind-css-v4-o8j</guid>
      <description>&lt;p&gt;I'am excited to introduce &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/tw-prose" rel="noopener noreferrer"&gt;&lt;code&gt;tw-prose&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — a &lt;strong&gt;CSS-only implementation of the Tailwind Typography plugin&lt;/strong&gt; built specifically for &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Typography is at the heart of every content-driven website, and &lt;code&gt;tw-prose&lt;/code&gt; makes it simple to get elegant, consistent text styling — without any plugin overhead.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Why &lt;code&gt;tw-prose&lt;/code&gt;?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Lightweight&lt;/strong&gt; – no JavaScript, no plugin complexity
&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;Beautiful defaults&lt;/strong&gt; – headings, paragraphs, lists, code blocks, tables, blockquotes, and more
&lt;/li&gt;
&lt;li&gt;🌓 &lt;strong&gt;Dark mode ready&lt;/strong&gt; – just add &lt;code&gt;prose-invert&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;📏 &lt;strong&gt;Responsive variants&lt;/strong&gt; – &lt;code&gt;prose-sm&lt;/code&gt;, &lt;code&gt;prose-lg&lt;/code&gt;, &lt;code&gt;prose-xl&lt;/code&gt;, &lt;code&gt;prose-2xl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Compatible with Tailwind v4&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Install &amp;amp; Go
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;tw-prose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tw-prose"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use it in your HTML with the &lt;code&gt;prose&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"prose"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello, world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Typography made simple.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Perfect For
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs and content-heavy sites
&lt;/li&gt;
&lt;li&gt;Projects where &lt;strong&gt;bundle size&lt;/strong&gt; matters
&lt;/li&gt;
&lt;li&gt;Teams that want &lt;strong&gt;zero-config typography&lt;/strong&gt; out of the box
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tw-prose&lt;/code&gt; is available now on &lt;strong&gt;npm&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://www.npmjs.com/package/tw-prose" rel="noopener noreferrer"&gt;npmjs.com/package/tw-prose&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Try it today and make your text shine with effortless typography in Tailwind CSS v4.  &lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>v4</category>
      <category>typography</category>
    </item>
    <item>
      <title>🚀 Ng New Command Generator</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Sat, 30 Aug 2025 10:02:35 +0000</pubDate>
      <link>https://dev.to/gridou/ng-new-command-generator-18j5</link>
      <guid>https://dev.to/gridou/ng-new-command-generator-18j5</guid>
      <description>&lt;p&gt;Starting a new Angular project always begins with &lt;code&gt;ng new&lt;/code&gt;, but remembering all the flags (routing, styles, strict mode, standalone, etc.) can be tricky.  &lt;/p&gt;

&lt;p&gt;That’s why I built the &lt;strong&gt;&lt;a href="https://ng.gridatek.com/" rel="noopener noreferrer"&gt;Ng New Command Generator&lt;/a&gt;&lt;/strong&gt; — a small tool for the Angular community that makes project setup super easy.  &lt;/p&gt;

&lt;p&gt;✅ Enter your project name&lt;br&gt;&lt;br&gt;
✅ Pick your options (routing, style, strict, standalone)&lt;br&gt;&lt;br&gt;
✅ Copy the ready-to-use command  &lt;/p&gt;

&lt;p&gt;The best part? Your choices are &lt;strong&gt;saved in local storage&lt;/strong&gt;, so next time you visit, your defaults are already there.  &lt;/p&gt;

&lt;p&gt;👉 Try it now: &lt;a href="https://ng.gridatek.com/" rel="noopener noreferrer"&gt;ng.gridatek.com&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>angular</category>
      <category>cli</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Deploy Angular App to Cloudflare Pages using GitHub Actions</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Wed, 20 Aug 2025 18:16:41 +0000</pubDate>
      <link>https://dev.to/gridou/deploy-angular-app-to-cloudflare-pages-37k9</link>
      <guid>https://dev.to/gridou/deploy-angular-app-to-cloudflare-pages-37k9</guid>
      <description>&lt;p&gt;This guide walks you through creating a new Angular application and deploying it to Cloudflare Pages using automated GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Build
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Important: Choose Your Own App Name&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since this is a template guide, you'll need to pick your own unique project names. Throughout this guide, we use &lt;code&gt;your-app-name&lt;/code&gt; as a placeholder - replace it with your chosen name everywhere it appears.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular App Name&lt;/strong&gt;: &lt;code&gt;your-app-name&lt;/code&gt; (choose your unique name)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Project&lt;/strong&gt;: &lt;code&gt;your-app-name&lt;/code&gt; (same as above)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;code&gt;your-app-name&lt;/code&gt; (same as above)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live URL&lt;/strong&gt;: &lt;code&gt;https://your-app-name.pages.dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js installed locally&lt;/li&gt;
&lt;li&gt;GitHub account&lt;/li&gt;
&lt;li&gt;Cloudflare account (free)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create New Angular Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Install Angular CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Angular CLI globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @angular/cli

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
ng version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Create New Angular App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create new Angular application (replace 'your-app-name' with your chosen name)&lt;/span&gt;
ng new your-app-name

&lt;span class="c"&gt;# You'll be prompted with options:&lt;/span&gt;
&lt;span class="c"&gt;# ? Which stylesheet format would you like to use? CSS [ https://developer.mozilla.org/docs/Web/CSS]&lt;/span&gt;
&lt;span class="c"&gt;# ? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No&lt;/span&gt;
&lt;span class="c"&gt;# ? Do you want to create a 'zoneless' application without zone.js? No&lt;/span&gt;
&lt;span class="c"&gt;# ? Which AI tools do you want to configure with Angular best practices? https://angular.dev/ai/develop-with-ai None&lt;/span&gt;

&lt;span class="c"&gt;# Navigate to project directory (use your chosen app name)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;your-app-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 Test Your App Locally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start development server&lt;/span&gt;
ng serve

&lt;span class="c"&gt;# Open browser to http://localhost:4200&lt;/span&gt;
&lt;span class="c"&gt;# You should see the Angular welcome page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Setup Cloudflare Account &amp;amp; API Token
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Create Cloudflare Account
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://cloudflare.com" rel="noopener noreferrer"&gt;cloudflare.com&lt;/a&gt; and sign up&lt;/li&gt;
&lt;li&gt;Verify your email address&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 Create API Token
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Dashboard&lt;/strong&gt; → &lt;strong&gt;My Profile&lt;/strong&gt; → &lt;strong&gt;API Tokens&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Token&lt;/strong&gt; → &lt;strong&gt;Create Custom Token&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure the token:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Token Name: Angular App Deploy
   Permissions:
   - Account - Cloudflare Pages:Edit

   Account Resources: Include - Your Account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Continue to Summary&lt;/strong&gt; → &lt;strong&gt;Create Token&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy and save the token&lt;/strong&gt; (you won't see it again!)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.3 Get Account ID
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Cloudflare Dashboard&lt;/li&gt;
&lt;li&gt;In the right sidebar, copy your &lt;strong&gt;Account ID&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Create Cloudflare Pages Project
&lt;/h2&gt;

&lt;p&gt;To set up a Cloudflare Pages project for your Angular app using Wrangler CLI, follow these steps. Wrangler is the official Cloudflare CLI tool that lets you create, manage, and deploy Pages projects quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Wrangler globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; wrangler

&lt;span class="c"&gt;# Authenticate with Cloudflare&lt;/span&gt;
wrangler login

&lt;span class="c"&gt;# Create your Pages project (replace 'your-app-name' with your chosen name)&lt;/span&gt;
wrangler pages project create your-app-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During setup, Wrangler will ask for the production branch name. Enter &lt;code&gt;main&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Initialize Git and Setup Repository
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Initialize Git Repository
&lt;/h3&gt;

&lt;p&gt;The Angular CLI automatically initializes a Git repository when you create a new project.&lt;br&gt;
You can confirm this by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the repository is not initialized, you can set it up manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Create GitHub Repository
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://github.com" rel="noopener noreferrer"&gt;github.com&lt;/a&gt; and create a new repository&lt;/li&gt;
&lt;li&gt;Name it: &lt;code&gt;your-app-name&lt;/code&gt; (same as your Angular app - use your chosen name)&lt;/li&gt;
&lt;li&gt;Don't initialize with README (we already have files)&lt;/li&gt;
&lt;li&gt;Copy the repository URL&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.3 Connect Local Repository to GitHub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add remote origin (replace YOUR_USERNAME and your-app-name with your values)&lt;/span&gt;
git remote add origin https://github.com/YOUR_USERNAME/your-app-name.git

&lt;span class="c"&gt;# Push to GitHub&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Setup GitHub Secrets
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to your GitHub repository&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Secrets and Variables&lt;/strong&gt; → &lt;strong&gt;Actions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New repository secret&lt;/strong&gt; and add:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;CLOUDFLARE_API_TOKEN&lt;/code&gt;, Value: Your API token&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;CLOUDFLARE_ACCOUNT_ID&lt;/code&gt;, Value: Your Account ID&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6: Create GitHub Action Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Create Deployment Workflow
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/deploy-cloudflare.yml&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Important: Replace &lt;code&gt;your-app-name&lt;/code&gt; with your actual app name in the YAML below&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloudflare Pages&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Angular App&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;22'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Angular app&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloudflare Pages&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloudflare/wrangler-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;apiToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLOUDFLARE_API_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;accountId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CLOUDFLARE_ACCOUNT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pages deploy dist/your-app-name/browser --project-name=your-app-name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Commit and Push Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add the workflow file&lt;/span&gt;
git add .github/workflows/deploy-cloudflare.yml

&lt;span class="c"&gt;# Commit the workflow&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add Cloudflare Pages deployment workflow"&lt;/span&gt;

&lt;span class="c"&gt;# Push to trigger first deployment&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Deploy Your App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 Monitor Deployment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your GitHub repository → &lt;strong&gt;Actions&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Watch the "Deploy to Cloudflare Pages" workflow run&lt;/li&gt;
&lt;li&gt;Check Cloudflare Dashboard → &lt;strong&gt;Workers &amp;amp; Pages&lt;/strong&gt; → &lt;strong&gt;your-app-name&lt;/strong&gt; for deployment status&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7.2 Access Your Live App
&lt;/h3&gt;

&lt;p&gt;Once deployed, your app will be available at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare URL&lt;/strong&gt;: &lt;code&gt;https://your-app-name.pages.dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 8: Example Implementation
&lt;/h2&gt;

&lt;p&gt;You can see a working example of this setup in this public repository (but remember to use your own app name):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example Repository&lt;/strong&gt;: &lt;code&gt;https://github.com/gridatek/angular-cloudflare-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>cloudflare</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
    <item>
      <title>No more PEM files in Spring Boot – Load SSL certs straight from Vault</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Fri, 08 Aug 2025 17:04:00 +0000</pubDate>
      <link>https://dev.to/gridou/no-more-pem-files-in-spring-boot-load-ssl-certs-straight-from-vault-4l9i</link>
      <guid>https://dev.to/gridou/no-more-pem-files-in-spring-boot-load-ssl-certs-straight-from-vault-4l9i</guid>
      <description>&lt;p&gt;Hey folks,&lt;/p&gt;

&lt;p&gt;I made a small library that lets your Spring Boot app load SSL certificates directly from HashiCorp Vault — no need to download or manage .crt/.key files yourself.&lt;/p&gt;

&lt;p&gt;🔗 Code: &lt;a href="https://github.com/gridadev/spring-vault-ssl-bundle" rel="noopener noreferrer"&gt;https://github.com/gridadev/spring-vault-ssl-bundle&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🧪 Demo: &lt;a href="https://github.com/khalilou88/spring-vault-ssl-bundle-demo" rel="noopener noreferrer"&gt;https://github.com/khalilou88/spring-vault-ssl-bundle-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works with Spring Boot's built-in &lt;code&gt;ssl.bundle&lt;/code&gt; config (3.2+). Just point it to your Vault path in YAML and you're done.&lt;/p&gt;

&lt;p&gt;✅ No file handling&lt;br&gt;&lt;br&gt;
✅ No scripts&lt;br&gt;&lt;br&gt;
✅ Auto-ready for cert rotation&lt;br&gt;&lt;br&gt;
✅ Works for client and server SSL&lt;/p&gt;

&lt;p&gt;Try it out and let me know what you think!&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>vault</category>
      <category>ssl</category>
      <category>java</category>
    </item>
    <item>
      <title>PEM vs. JKS: Why Modern Spring Boot Apps Are Ditching KeyStores</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Mon, 04 Aug 2025 14:20:57 +0000</pubDate>
      <link>https://dev.to/gridou/pem-vs-jks-why-modern-spring-boot-apps-are-ditching-keystores-396e</link>
      <guid>https://dev.to/gridou/pem-vs-jks-why-modern-spring-boot-apps-are-ditching-keystores-396e</guid>
      <description>&lt;p&gt;As the Java ecosystem evolves, Spring Boot developers face a critical choice when it comes to TLS/SSL configuration: PEM or JKS? Historically, Java KeyStore (JKS) has been the default. But with the introduction of SSL bundles in Spring Boot 3.1+, PEM is emerging as the smarter, more interoperable option.&lt;/p&gt;

&lt;p&gt;In this article, we’ll break down the differences between PEM and JKS and explain why PEM should be your go-to format for certificates and keys in modern Spring Boot projects.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 What’s the Difference?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PEM&lt;/th&gt;
&lt;th&gt;JKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text-based (Base64 encoded)&lt;/td&gt;
&lt;td&gt;Binary Java-specific format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenSSL, widely supported across platforms&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;keytool&lt;/code&gt; (Java-specific)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (used in NGINX, Apache, Kubernetes, etc.)&lt;/td&gt;
&lt;td&gt;Low (mainly Java)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human Readable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spring Boot Support Before 3.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Supported via &lt;code&gt;server.ssl.*&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spring Boot Support After 3.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Native via &lt;code&gt;spring.ssl.bundle.pem&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Supported via &lt;code&gt;server.ssl.*&lt;/code&gt; and &lt;code&gt;spring.ssl.bundle.pem&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conversion Required?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes if integrating with non-Java systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps/Cloud Friendly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Very&lt;/td&gt;
&lt;td&gt;❌ Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  ✅ Why PEM is the Better Choice
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interoperability Across Platforms&lt;/strong&gt;&lt;br&gt;
PEM is the universal format for TLS in Kubernetes, Docker, NGINX, Istio, cert-manager, and more. It plays well in hybrid environments where your Spring Boot app needs to coexist with services written in Python, Go, or Node.js.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No More Format Conversion&lt;/strong&gt;&lt;br&gt;
With JKS, you're often forced to convert certificates using &lt;code&gt;keytool&lt;/code&gt; or &lt;code&gt;openssl&lt;/code&gt; just to get Java to understand them. PEM eliminates that friction—no unnecessary conversions, no extra tooling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;First-Class Spring Boot Support&lt;/strong&gt;&lt;br&gt;
Starting with Spring Boot 3.1, PEM is natively supported via &lt;code&gt;spring.ssl.bundle.pem&lt;/code&gt;, allowing clean separation of private keys, certificates, and trust chains—all without touching &lt;code&gt;keytool&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better Transparency and Auditing&lt;/strong&gt;&lt;br&gt;
Since PEM files are human-readable, it’s easier to inspect certificates, audit chains of trust, and debug SSL issues. Compare that to the opaque &lt;code&gt;.jks&lt;/code&gt; binary format, where you’re dependent on tooling to explore its contents.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  🔄 But What About JKS?
&lt;/h3&gt;

&lt;p&gt;JKS still has a few legacy advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It integrates seamlessly with older Java EE servers.&lt;/li&gt;
&lt;li&gt;Some Java tools and libraries still expect &lt;code&gt;.jks&lt;/code&gt; or &lt;code&gt;.p12&lt;/code&gt; formats.&lt;/li&gt;
&lt;li&gt;Existing enterprise setups may be deeply tied to JKS conventions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, in a modern Spring Boot environment, especially for cloud-native and microservice-based apps, these advantages are increasingly irrelevant.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 How to Use PEM in Spring Boot
&lt;/h3&gt;

&lt;p&gt;Example using SSL Bundle (Spring Boot 3.1+):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-pem-bundle&lt;/span&gt;
&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;my-pem-bundle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;certificate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:certs/cert.pem&lt;/span&gt;
          &lt;span class="na"&gt;private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:certs/key.pem&lt;/span&gt;
          &lt;span class="na"&gt;trust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;certificate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classpath:certs/ca.pem&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it—no keytool, no conversions, no headaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;In 2025, there's no good reason to stay locked into Java-only formats like JKS. PEM is portable, readable, flexible, and natively supported in Spring Boot. It’s the clear choice for teams building modern, secure, cloud-native Java applications.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>jks</category>
      <category>pem</category>
    </item>
    <item>
      <title>Install Hadoop on Fedora</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Thu, 17 Jul 2025 21:42:50 +0000</pubDate>
      <link>https://dev.to/gridou/install-hadoop-on-fedora-1ndh</link>
      <guid>https://dev.to/gridou/install-hadoop-on-fedora-1ndh</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;First, ensure you have Java installed since Hadoop requires it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install OpenJDK 8 or 11 (Hadoop works well with both)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;java-11-openjdk java-11-openjdk-devel

&lt;span class="c"&gt;# Verify Java installation&lt;/span&gt;
java &lt;span class="nt"&gt;-version&lt;/span&gt;
javac &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Download and Install Hadoop
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Download Hadoop:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a directory for Hadoop&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/hadoop
&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp

&lt;span class="c"&gt;# Download Hadoop (replace with latest stable version)&lt;/span&gt;
wget https://archive.apache.org/dist/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz

&lt;span class="c"&gt;# Extract and move to /opt&lt;/span&gt;
&lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; hadoop-3.3.6.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /opt/hadoop &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# Change ownership&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /opt/hadoop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up environment variables:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit your ~/.bashrc file&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export HADOOP_HOME=/opt/hadoop'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc

&lt;span class="c"&gt;# Reload the configuration&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Configure Hadoop:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Edit the Hadoop configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set JAVA_HOME in Hadoop's environment&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/hadoop-env.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Configuration (Pseudo-distributed mode)
&lt;/h2&gt;

&lt;p&gt;For a single-node setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure core-site.xml:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/core-site.xml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;fs.defaultFS&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;hdfs://localhost:9000&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure hdfs-site.xml:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop/hdfs-site.xml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;dfs.replication&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;1&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;dfs.namenode.name.dir&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;file:///opt/hadoop/data/namenode&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
    &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;dfs.datanode.data.dir&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;file:///opt/hadoop/data/datanode&amp;lt;/value&amp;gt;
    &amp;lt;/property&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create data directories:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/hadoop/data/namenode
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/hadoop/data/datanode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up SSH (required for Hadoop)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install SSH server if not already installed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;openssh-server

&lt;span class="c"&gt;# Generate SSH key pair&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class="c"&gt;# Add key to authorized_keys&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;0600 ~/.ssh/authorized_keys

&lt;span class="c"&gt;# Start SSH service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start sshd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialize and Start Hadoop
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Format the namenode:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hdfs namenode &lt;span class="nt"&gt;-format&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Hadoop services:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start HDFS&lt;/span&gt;
start-dfs.sh

&lt;span class="c"&gt;# Start YARN (optional, for resource management)&lt;/span&gt;
start-yarn.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify installation:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check running processes&lt;/span&gt;
jps

&lt;span class="c"&gt;# Check HDFS status&lt;/span&gt;
hdfs dfsadmin &lt;span class="nt"&gt;-report&lt;/span&gt;

&lt;span class="c"&gt;# Access web interfaces:&lt;/span&gt;
&lt;span class="c"&gt;# NameNode: http://localhost:9870&lt;/span&gt;
&lt;span class="c"&gt;# ResourceManager: http://localhost:8088&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Hadoop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a directory in HDFS&lt;/span&gt;
hdfs dfs &lt;span class="nt"&gt;-mkdir&lt;/span&gt; /user
hdfs dfs &lt;span class="nt"&gt;-mkdir&lt;/span&gt; /user/&lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Put a file into HDFS&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello Hadoop"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; test.txt
hdfs dfs &lt;span class="nt"&gt;-put&lt;/span&gt; test.txt /user/&lt;span class="nv"&gt;$USER&lt;/span&gt;/

&lt;span class="c"&gt;# List files in HDFS&lt;/span&gt;
hdfs dfs &lt;span class="nt"&gt;-ls&lt;/span&gt; /user/&lt;span class="nv"&gt;$USER&lt;/span&gt;/

&lt;span class="c"&gt;# Get file from HDFS&lt;/span&gt;
hdfs dfs &lt;span class="nt"&gt;-get&lt;/span&gt; /user/&lt;span class="nv"&gt;$USER&lt;/span&gt;/test.txt downloaded_test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stop Hadoop
&lt;/h2&gt;

&lt;p&gt;When you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stop-yarn.sh
stop-dfs.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup gives you a working single-node Hadoop cluster on Fedora. For production or multi-node clusters, you'll need additional configuration for security, networking, and resource management.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hive Internal vs External Tables</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Thu, 17 Jul 2025 12:28:17 +0000</pubDate>
      <link>https://dev.to/gridou/hive-internal-vs-external-tables-33gd</link>
      <guid>https://dev.to/gridou/hive-internal-vs-external-tables-33gd</guid>
      <description>&lt;h2&gt;
  
  
  Internal Tables (Managed Tables)
&lt;/h2&gt;

&lt;p&gt;Internal tables are &lt;strong&gt;managed by Hive&lt;/strong&gt;, meaning Hive controls both the metadata and the underlying data files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hive manages the complete lifecycle of the table and its data&lt;/li&gt;
&lt;li&gt;Data is stored in Hive's warehouse directory (typically &lt;code&gt;/user/hive/warehouse/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;When you DROP the table, both metadata and data are deleted&lt;/li&gt;
&lt;li&gt;Hive has full control over the data location and format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  External Tables
&lt;/h2&gt;

&lt;p&gt;External tables are &lt;strong&gt;not managed by Hive&lt;/strong&gt; - Hive only manages the metadata while the data remains in its original location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hive only manages table metadata, not the actual data&lt;/li&gt;
&lt;li&gt;Data can be stored anywhere in HDFS or other file systems&lt;/li&gt;
&lt;li&gt;When you DROP the table, only metadata is deleted, data remains intact&lt;/li&gt;
&lt;li&gt;Useful for sharing data with other systems or when data is managed externally&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Differences Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Internal Table&lt;/th&gt;
&lt;th&gt;External Table&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed by Hive&lt;/td&gt;
&lt;td&gt;Managed externally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hive warehouse directory&lt;/td&gt;
&lt;td&gt;Any HDFS location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DROP Behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deletes both metadata and data&lt;/td&gt;
&lt;td&gt;Deletes only metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Sharing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Difficult to share with other systems&lt;/td&gt;
&lt;td&gt;Easy to share with other systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hive-only data processing&lt;/td&gt;
&lt;td&gt;Data shared across multiple systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slightly better (optimized location)&lt;/td&gt;
&lt;td&gt;Depends on location and access patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to Use Each
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Internal Tables when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is exclusively used by Hive&lt;/li&gt;
&lt;li&gt;You want Hive to manage the complete data lifecycle&lt;/li&gt;
&lt;li&gt;You need maximum performance optimization&lt;/li&gt;
&lt;li&gt;Data doesn't need to be shared with other systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use External Tables when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is shared with other systems (Spark, MapReduce, etc.)&lt;/li&gt;
&lt;li&gt;You want to preserve data when dropping tables&lt;/li&gt;
&lt;li&gt;Data is managed by external ETL processes&lt;/li&gt;
&lt;li&gt;You're working with existing data that shouldn't be moved&lt;/li&gt;
&lt;li&gt;You need to point to data in different locations or formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;External tables provide more flexibility and are commonly used in enterprise environments where data needs to be accessed by multiple tools and systems.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Streamlined Git Flow: Managing Projects with Master, Feature, and Delivery Branches</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Mon, 12 May 2025 08:52:36 +0000</pubDate>
      <link>https://dev.to/gridou/streamlined-git-flow-managing-projects-with-master-feature-and-delivery-branches-46m3</link>
      <guid>https://dev.to/gridou/streamlined-git-flow-managing-projects-with-master-feature-and-delivery-branches-46m3</guid>
      <description>&lt;p&gt;&lt;em&gt;A Practical Approach to Git Branching that Maintains Code Integrity While Preserving Release History&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Version control is the backbone of modern software development, and having a clear, consistent Git workflow is essential for team productivity and code quality. This article presents a streamlined Git flow that balances simplicity with robustness, using three primary branch types: &lt;code&gt;master&lt;/code&gt;, &lt;code&gt;feature&lt;/code&gt;, and &lt;code&gt;delivery&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Branch Structure
&lt;/h2&gt;

&lt;p&gt;Our Git flow revolves around four key branch types, each with a specific purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Master Branch&lt;/strong&gt;: The main branch where active development occurs, serving as the integration point and the source of truth for your codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature Branches&lt;/strong&gt;: Short-lived branches for developing new functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Delivery Branches&lt;/strong&gt;: Long-lived branches that capture the state of each release.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hotfix Branches&lt;/strong&gt;: Branches created to quickly address and fix critical issues in a released version, ensuring immediate resolution without disrupting ongoing development.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. The Workflow in Detail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Starting a New Feature
&lt;/h3&gt;

&lt;p&gt;Every new development task begins by creating a feature branch from the latest &lt;code&gt;master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout master
git pull
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/my-new-feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures your feature is based on the most current codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Feature Development
&lt;/h3&gt;

&lt;p&gt;As you develop, commit your changes regularly to your feature branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Implement specific functionality"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid painful rebase conflicts later, regularly incorporate changes from &lt;code&gt;master&lt;/code&gt; by rebasing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feature/my-new-feature
git fetch origin
git rebase origin/master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebasing instead of merging keeps the history linear and avoids unnecessary merge commits, which can clutter the history.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. Completing Your Feature
&lt;/h3&gt;

&lt;p&gt;When your feature is ready for review, push it to the remote repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin feature/my-new-feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a pull request to merge your feature into &lt;code&gt;master&lt;/code&gt;. This step often involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review by team members&lt;/li&gt;
&lt;li&gt;Running automated tests&lt;/li&gt;
&lt;li&gt;Making requested changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: &lt;strong&gt;Before submitting the pull request&lt;/strong&gt;, it's good practice to rebase the feature branch onto the latest &lt;code&gt;master&lt;/code&gt; to ensure your changes are based on the most up-to-date version of the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feature/my-new-feature
git fetch origin
git rebase origin/master
git push &lt;span class="nt"&gt;--force-with-lease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the pull request will apply cleanly and reduce potential merge conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4. Managing Releases
&lt;/h3&gt;

&lt;p&gt;When preparing for a release, it's important to handle versioning carefully, ensuring each phase—next version, release candidate, and final version—has its own distinct marker.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.4.1. Next Version Tagging
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;next version&lt;/strong&gt; refers to the ongoing work for a new release and can be tracked using the &lt;code&gt;-next&lt;/code&gt; suffix. This version helps developers track continuous progress during the development cycle, signifying that the code is still evolving but intended for a future release.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.1.1. Version Tagging Format:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: &lt;code&gt;v1.0.0-next-0&lt;/code&gt;, &lt;code&gt;v1.0.0-next-1&lt;/code&gt;, &lt;code&gt;v1.0.0-next-2&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;-next&lt;/code&gt; suffix is followed by a sequential number that increments with each iteration or set of changes in the code. This makes it easy to distinguish between different stages of the development process.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.1.2. Creating and Pushing a Next Version Tag:
&lt;/h5&gt;

&lt;h6&gt;
  
  
  3.4.1.2.1. Update Master Branch:
&lt;/h6&gt;

&lt;p&gt;Make sure your local &lt;code&gt;master&lt;/code&gt; branch is up-to-date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout master
git pull origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.1.2.2. Create the Next Version Tag:
&lt;/h6&gt;

&lt;p&gt;Tag the &lt;code&gt;master&lt;/code&gt; branch with the next version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.0.0-next-0
git push origin v1.0.0-next-0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.1.2.3. Incrementing the &lt;code&gt;next&lt;/code&gt; Version:
&lt;/h6&gt;

&lt;p&gt;Each time a new iteration is made, update the &lt;code&gt;-next&lt;/code&gt; suffix to reflect the next step in development, e.g., &lt;code&gt;v1.0.0-next-1&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.4.2. Release Candidate (RC) Versioning
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;release candidate (RC)&lt;/strong&gt; is a stable, pre-release version that has passed all functional testing but may still need some validation. This version indicates that the code is ready for production but may require final tweaks.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.2.1. Version Tagging Format:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: &lt;code&gt;v1.0.0-rc-0&lt;/code&gt;, &lt;code&gt;v1.0.0-rc-1&lt;/code&gt;, &lt;code&gt;v1.0.0-rc-2&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similar to the &lt;code&gt;next&lt;/code&gt; version, RC versions increment sequentially as new release candidates are tested and prepared for final release.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.2.2. Creating and Pushing an RC Tag:
&lt;/h5&gt;

&lt;h6&gt;
  
  
  3.4.2.2.1. Create a Delivery Branch from Master:
&lt;/h6&gt;

&lt;p&gt;First, ensure that your delivery branch is ready by creating it from &lt;code&gt;master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout master
git pull origin master
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; delivery/v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.2.2.2. Final Testing:
&lt;/h6&gt;

&lt;p&gt;Perform final tests, fix bugs, and prepare documentation or version updates as necessary.&lt;/p&gt;

&lt;h6&gt;
  
  
  3.4.2.2.3. Tag the RC Version:
&lt;/h6&gt;

&lt;p&gt;Once the release candidate is stable, create an RC tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.0.0-rc-0
git push origin v1.0.0-rc-0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.2.2.4. Iterate Through RC Versions:
&lt;/h6&gt;

&lt;p&gt;If there are issues in the RC version, increment the &lt;code&gt;rc&lt;/code&gt; number with each new fix, e.g., &lt;code&gt;v1.0.0-rc-1&lt;/code&gt;, &lt;code&gt;v1.0.0-rc-2&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.4.3. Final Release Version
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;final release&lt;/strong&gt; version is the last step in the release cycle. It represents the version that is stable and ready for deployment to production.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.3.1. Version Tagging Format:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: &lt;code&gt;v1.0.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the release candidate has passed all tests and final adjustments have been made, the version is tagged as the final release version.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.4.3.2. Steps for Creating the Final Release Tag:
&lt;/h5&gt;

&lt;h6&gt;
  
  
  3.4.3.2.1. Finalize Delivery Branch:
&lt;/h6&gt;

&lt;p&gt;Once the RC is ready, finalize the delivery branch by ensuring it's up-to-date and stable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout delivery/v1.0.0
git pull origin delivery/v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.3.2.2. Create the Final Release Tag:
&lt;/h6&gt;

&lt;p&gt;Tag the final version with the &lt;code&gt;v1.0.0&lt;/code&gt; label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.0.0
git push origin v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  3.4.3.2.3. Do not merge the Delivery branch into Master:
&lt;/h6&gt;

&lt;p&gt;After creating the final release tag on your delivery branch, it's important to &lt;strong&gt;avoid merging the delivery branch back into master&lt;/strong&gt;. There are several critical reasons for this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintaining Release Integrity&lt;/strong&gt;: Delivery branches should remain as historical snapshots of what was released, unaltered by future development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preventing Feature Contamination&lt;/strong&gt;: Master typically evolves with new features and changes during the release stabilization period. These features weren't intended for the current release and shouldn't be mixed with released code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoiding Unexpected Side Effects&lt;/strong&gt;: Merging delivery branches back to master can introduce unexpected behavior, especially if master has significantly diverged since the delivery branch was created.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, when issues need to be fixed in a released version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the hotfix workflow described in Section 6&lt;/span&gt;
git checkout delivery/v1.0.0
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; hotfix/critical-issue-fix

&lt;span class="c"&gt;# After implementing and testing the fix&lt;/span&gt;
&lt;span class="c"&gt;# Apply to both the delivery branch and master separately&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach ensures clean separation between versions while still maintaining the ability to apply critical fixes where needed. It preserves the purpose of your branches: master for active development and delivery branches as immutable records of releases.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5. Housekeeping
&lt;/h3&gt;

&lt;p&gt;After a successful release, clean up your completed feature branches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; feature/my-new-feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Importantly, &lt;strong&gt;keep your delivery branches&lt;/strong&gt; as a permanent record of each release.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.6. Handling Hotfixes
&lt;/h3&gt;

&lt;p&gt;When a critical issue is discovered in a released version, start the fix directly from the relevant delivery branch. This ensures the fix is based on the exact code that was deployed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout delivery/v1.0.0
git pull
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; hotfix/critical-issue-fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After implementing and testing the fix, follow these steps:&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6.1. Update the Delivery Branch with the Hotfix
&lt;/h4&gt;

&lt;p&gt;Create a pull request to merge the hotfix into the delivery branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After committing your changes to the hotfix branch&lt;/span&gt;
git push origin hotfix/critical-issue-fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your team has reviewed and approved the hotfix, merge it into the delivery branch using your Git hosting platform (GitHub, GitLab, etc.).&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6.2. Tag the Updated Delivery Branch with a Patch Version
&lt;/h4&gt;

&lt;p&gt;After merging the hotfix, create a new patch version tag on the delivery branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout delivery/v1.0.0
git pull origin delivery/v1.0.0
git tag v1.0.1
git push origin v1.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new release version that includes your hotfix.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6.3. Apply the Hotfix to Master
&lt;/h4&gt;

&lt;p&gt;To ensure future releases also include this critical fix, create a separate pull request to apply the same fix to master:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new branch from master for the same fix&lt;/span&gt;
git checkout master
git pull origin master
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; hotfix/critical-issue-fix-master

&lt;span class="c"&gt;# Apply the same changes as in the original hotfix&lt;/span&gt;
&lt;span class="c"&gt;# This may require cherry-picking or manually implementing the same changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push this branch and create a pull request to master, ensuring the commit message references the original hotfix and issue number for traceability.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6.4. Clean Up Branches
&lt;/h4&gt;

&lt;p&gt;After both pull requests have been merged, clean up the temporary branches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; hotfix/critical-issue-fix
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; hotfix/critical-issue-fix-master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach ensures your hotfix is applied both to the released version (via the delivery branch) and to ongoing development (via master), while keeping these processes separate to maintain the integrity of each branch. It also preserves the delivery branch as an accurate historical record of what was released.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Benefits of This Approach
&lt;/h2&gt;

&lt;p&gt;4.1. &lt;strong&gt;Simplicity + Structure&lt;/strong&gt;: Using just three core branch types (with hotfixes as needed) keeps cognitive load low.&lt;/p&gt;

&lt;p&gt;4.2. &lt;strong&gt;Release Preservation&lt;/strong&gt;: Delivery branches act as immutable records of what was released—this is excellent for traceability and auditing.&lt;/p&gt;

&lt;p&gt;4.3. &lt;strong&gt;Rebase-Driven Clean History&lt;/strong&gt;: Encouraging rebasing over merging avoids tangled commit graphs and unnecessary noise in the log.&lt;/p&gt;

&lt;p&gt;4.4. &lt;strong&gt;Isolated Hotfix Workflow&lt;/strong&gt;: Handling hotfixes off the delivery branch directly is an elegant solution that minimizes risk to in-progress work.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;This streamlined Git flow provides a balanced approach that works well for many development teams. By incorporating &lt;code&gt;git rebase&lt;/code&gt; into your workflow, you ensure a cleaner, more linear commit history, which is easier to maintain and navigate. It also reduces merge commits, which can sometimes clutter the project's history. By following these straightforward patterns, your team can focus less on Git complexities and more on delivering great software.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Have you implemented a similar Git flow in your team? What challenges or improvements have you discovered? Share your experiences in the comments below.&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Properties Configuration Library for Angular in Nx Workspaces</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Sun, 27 Apr 2025 13:00:05 +0000</pubDate>
      <link>https://dev.to/gridou/building-a-properties-configuration-library-for-angular-in-nx-workspaces-24n6</link>
      <guid>https://dev.to/gridou/building-a-properties-configuration-library-for-angular-in-nx-workspaces-24n6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Configuration management is a critical aspect of any enterprise Angular application. Whether you're working with environment-specific settings, feature flags, or API endpoints, having a robust way to load and access configuration at runtime is essential. In this article, we'll build a properties configuration library specifically designed for Angular applications in Nx workspaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Traditional Angular configuration approaches using environment files have limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuration is bundled at build time, requiring rebuilds for config changes&lt;/li&gt;
&lt;li&gt;Different environments require separate builds&lt;/li&gt;
&lt;li&gt;Configuration can't be updated without redeploying the application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need a solution that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads configuration at runtime from a JSON file&lt;/li&gt;
&lt;li&gt;Enforces strict property access (fails fast on missing properties)&lt;/li&gt;
&lt;li&gt;Works seamlessly in an Nx monorepo environment&lt;/li&gt;
&lt;li&gt;Uses modern Angular patterns (standalone components, functional providers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Solution: A Properties Configuration Library
&lt;/h2&gt;

&lt;p&gt;Let's create a library for loading and accessing configuration from a &lt;code&gt;properties.json&lt;/code&gt; file at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Runtime loading of configuration from a JSON file&lt;/li&gt;
&lt;li&gt;Strict property access with clear error messages&lt;/li&gt;
&lt;li&gt;Support for nested properties using dot notation&lt;/li&gt;
&lt;li&gt;Application initialization integration&lt;/li&gt;
&lt;li&gt;TypeScript type safety&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. The Properties Service
&lt;/h4&gt;

&lt;p&gt;The heart of our solution is the &lt;code&gt;PropertiesService&lt;/code&gt; that handles loading and accessing configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// File: libs/properties/src/lib/properties.service.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;firstValueFrom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Properties&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PropertyNotFoundError&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Property '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' not found in configuration`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PropertyNotFoundError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PropertiesService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;defaultPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/properties.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;loadProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Properties&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultPath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;firstValueFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Properties&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load properties file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to load properties file from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Properties not loaded. Initialize the service first&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNestedProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PropertyNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;hasProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Properties not loaded. Initialize the service first&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNestedProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;isInitialized&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getAllProperties&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Properties&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Properties not loaded. Initialize the service first&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getNestedProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Application Initialization Provider
&lt;/h4&gt;

&lt;p&gt;To ensure properties are loaded before the application starts, we'll use Angular's &lt;code&gt;provideAppInitializer&lt;/code&gt; (available in Angular 16+):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// File: libs/properties/src/lib/properties.provider.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentProviders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;makeEnvironmentProviders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideHttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideAppInitializer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/appinit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./properties.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PropertiesConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;provideProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;PropertiesConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentProviders&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;makeEnvironmentProviders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;provideAppInitializer&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Library Exports
&lt;/h4&gt;

&lt;p&gt;We'll keep our public API clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// File: libs/properties/src/index.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/properties.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/properties.provider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Use the Library
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create a Properties JSON File
&lt;/h3&gt;

&lt;p&gt;Place a &lt;code&gt;properties.json&lt;/code&gt; file in your application's assets folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"apiUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableFeatureA"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableFeatureB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Configure Your Application
&lt;/h3&gt;

&lt;p&gt;In your application configuration, provide the properties service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.config.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideProperties&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@myorg/properties&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideProperties&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/properties.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use Properties in Components
&lt;/h3&gt;

&lt;p&gt;Now you can use the properties in your components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PropertyNotFoundError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@myorg/properties&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;API URL: {{ apiUrl }}&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;Log level: {{ logLevel }}&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;Feature A enabled: {{ featureAEnabled }}&amp;lt;/div&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PropertiesService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;logLevel&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;featureAEnabled&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logging.level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featureAEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features.enableFeatureA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;PropertyNotFoundError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing required property:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error accessing properties:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Strict Property Access
&lt;/h3&gt;

&lt;p&gt;Our library is designed to fail fast if a required property is missing. Instead of returning undefined or using default values that might mask configuration issues, we throw a specific &lt;code&gt;PropertyNotFoundError&lt;/code&gt;. This helps catch configuration problems early in development or deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Support for Nested Properties
&lt;/h3&gt;

&lt;p&gt;Configuration often has a hierarchical structure. Our library supports dot notation for accessing nested properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Access nested properties with dot notation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logging.level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;featureEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features.enableFeatureA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Application Initialization
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;provideAppInitializer&lt;/code&gt; to ensure that properties are loaded before the application starts. This prevents components from accessing configuration before it's available.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. TypeScript Type Safety
&lt;/h3&gt;

&lt;p&gt;The library leverages TypeScript generics to provide type safety when accessing properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Type-safe property access&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api.maxRetries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;featureEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features.newFeature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Checking If a Property Exists
&lt;/h3&gt;

&lt;p&gt;Sometimes you need to check if an optional property exists before using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features.experimentalFeature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Use the experimental feature&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;experimentalConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features.experimentalFeature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Getting All Properties
&lt;/h3&gt;

&lt;p&gt;You might need to access all properties at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;propertiesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAllProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Current configuration:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment Considerations
&lt;/h2&gt;

&lt;p&gt;With this library, you can deploy the same build to different environments and simply change the &lt;code&gt;properties.json&lt;/code&gt; file. This simplifies your CI/CD pipeline and reduces the number of builds required.&lt;/p&gt;

&lt;p&gt;For cloud deployments, you could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Include environment-specific &lt;code&gt;properties.json&lt;/code&gt; files in your Docker images&lt;/li&gt;
&lt;li&gt;Mount configuration files from environment-specific ConfigMaps in Kubernetes&lt;/li&gt;
&lt;li&gt;Use environment-specific S3 buckets or Azure Blob Storage to host configuration files&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Our Properties Configuration Library provides a robust solution for runtime configuration management in Angular applications within Nx workspaces. By loading configuration at runtime, enforcing strict property access, and leveraging modern Angular patterns, we've created a flexible and maintainable approach to configuration management.&lt;/p&gt;

&lt;p&gt;This library allows for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing configuration without rebuilding applications&lt;/li&gt;
&lt;li&gt;Catching missing configuration early&lt;/li&gt;
&lt;li&gt;Accessing nested properties with ease&lt;/li&gt;
&lt;li&gt;Type-safe property access&lt;/li&gt;
&lt;li&gt;Clean integration with Angular's initialization process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features make it an excellent choice for enterprise Angular applications in Nx workspaces where configuration management is critical.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Spring Boot with Spotless and Git Pre-commit Hooks</title>
      <dc:creator>khalil la</dc:creator>
      <pubDate>Fri, 25 Apr 2025 07:45:13 +0000</pubDate>
      <link>https://dev.to/gridou/spring-boot-with-spotless-and-git-pre-commit-hooks-1h4f</link>
      <guid>https://dev.to/gridou/spring-boot-with-spotless-and-git-pre-commit-hooks-1h4f</guid>
      <description>&lt;p&gt;This guide walks you through setting up automated code formatting and pre-commit checks in a Spring Boot Maven project, ensuring consistent code style across your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Step 1: Set Up a Spring Boot Project&lt;/li&gt;
&lt;li&gt;Step 2: Configure Spotless Maven Plugin&lt;/li&gt;
&lt;li&gt;Step 3: Install Git Hooks with Maven&lt;/li&gt;
&lt;li&gt;Step 4: Adding Git Commit ID Plugin&lt;/li&gt;
&lt;li&gt;Step 5: Full POM Example&lt;/li&gt;
&lt;li&gt;Step 6: Using the Configuration&lt;/li&gt;
&lt;li&gt;Step 7: Customization Options&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Having consistent code style across a project improves readability and maintainability. This guide combines three powerful tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spotless&lt;/strong&gt;: A code formatter that enforces style rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git Pre-commit Hooks&lt;/strong&gt;: Automatically checks or formats code before commits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maven&lt;/strong&gt;: Integrates these tools into your build process&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Java 8 or higher&lt;/li&gt;
&lt;li&gt;Maven 3.6.0 or higher&lt;/li&gt;
&lt;li&gt;Git installed and initialized in your project&lt;/li&gt;
&lt;li&gt;A Spring Boot project (or willingness to create one)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up a Spring Boot Project
&lt;/h2&gt;

&lt;p&gt;If you don't already have a Spring Boot project, create one using &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt; or with the following Maven command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn archetype:generate &lt;span class="nt"&gt;-DgroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example &lt;span class="nt"&gt;-DartifactId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo &lt;span class="nt"&gt;-DarchetypeArtifactId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maven-archetype-quickstart &lt;span class="nt"&gt;-DarchetypeVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.4 &lt;span class="nt"&gt;-DinteractiveMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to initialize Git in your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Configure Spotless Maven Plugin
&lt;/h2&gt;

&lt;p&gt;Add the Spotless Maven plugin to your &lt;code&gt;pom.xml&lt;/code&gt; file in the &lt;code&gt;&amp;lt;build&amp;gt;&amp;lt;plugins&amp;gt;&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.diffplug.spotless&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spotless-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.40.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/main/java/**/*.java&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/test/java/**/*.java&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;googleJavaFormat&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.16.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;GOOGLE&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/googleJavaFormat&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;removeUnusedImports&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;importOrder&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;order&amp;gt;&lt;/span&gt;java,javax,org,com,&lt;span class="nt"&gt;&amp;lt;/order&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/importOrder&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;check&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Google's Java code style&lt;/li&gt;
&lt;li&gt;Removes unused imports&lt;/li&gt;
&lt;li&gt;Organizes imports in a specific order&lt;/li&gt;
&lt;li&gt;Checks formatting during compilation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Install Git Hooks with Maven
&lt;/h2&gt;

&lt;p&gt;Add the Git Build Hook Maven plugin to manage Git hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.rudikershaw.gitbuildhook&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;git-build-hook-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.4.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;preCommit&amp;gt;&lt;/span&gt;mvn spotless:apply&lt;span class="nt"&gt;&amp;lt;/preCommit&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;install&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;initialize&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installs Git hooks during the initialize phase&lt;/li&gt;
&lt;li&gt;Sets up a pre-commit hook that runs &lt;code&gt;mvn spotless:apply&lt;/code&gt; before each commit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Adding Git Commit ID Plugin
&lt;/h2&gt;

&lt;p&gt;Add the Git Commit ID plugin to include Git information in your build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.git-commit-id&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;git-commit-id-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;6.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;get-the-git-infos&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;revision&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;initialize&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;generateGitPropertiesFile&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/generateGitPropertiesFile&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;generateGitPropertiesFilename&amp;gt;&lt;/span&gt;${project.build.outputDirectory}/git.properties&lt;span class="nt"&gt;&amp;lt;/generateGitPropertiesFilename&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;includeOnlyProperties&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.branch$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.commit.id$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.commit.time$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/includeOnlyProperties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds Git metadata to your build, which can be useful for tracking versions and build information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Full POM Example
&lt;/h2&gt;

&lt;p&gt;Here's a complete &lt;code&gt;pom.xml&lt;/code&gt; example for a Spring Boot project with all these configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://maven.apache.org/POM/4.0.0"&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
         &lt;span class="na"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;modelVersion&amp;gt;&lt;/span&gt;4.0.0&lt;span class="nt"&gt;&amp;lt;/modelVersion&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.2.3&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;relativePath/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.example&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;demo&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.1-SNAPSHOT&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;demo&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Demo Spring Boot project with Spotless and Git hooks&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Spotless Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.diffplug.spotless&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spotless-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.40.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;java&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/main/java/**/*.java&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/test/java/**/*.java&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;googleJavaFormat&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.16.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;GOOGLE&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/googleJavaFormat&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;removeUnusedImports&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;importOrder&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;order&amp;gt;&lt;/span&gt;java,javax,org,com,&lt;span class="nt"&gt;&amp;lt;/order&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/importOrder&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;check&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Git Commit ID Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.git-commit-id&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;git-commit-id-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;6.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;get-the-git-infos&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;revision&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;initialize&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;generateGitPropertiesFile&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/generateGitPropertiesFile&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;generateGitPropertiesFilename&amp;gt;&lt;/span&gt;${project.build.outputDirectory}/git.properties&lt;span class="nt"&gt;&amp;lt;/generateGitPropertiesFilename&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;includeOnlyProperties&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.branch$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.commit.id$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;includeOnlyProperty&amp;gt;&lt;/span&gt;^git.commit.time$&lt;span class="nt"&gt;&amp;lt;/includeOnlyProperty&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/includeOnlyProperties&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Git Hook Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.rudikershaw.gitbuildhook&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;git-build-hook-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.4.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;preCommit&amp;gt;&lt;/span&gt;mvn spotless:apply&lt;span class="nt"&gt;&amp;lt;/preCommit&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;install&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;initialize&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Using the Configuration
&lt;/h2&gt;

&lt;p&gt;After adding these configurations to your &lt;code&gt;pom.xml&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Git hooks:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn initialize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify the pre-commit hook is installed:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .git/hooks/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something that includes the command &lt;code&gt;mvn spotless:apply&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Now when you try to commit code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pre-commit hook will automatically run &lt;code&gt;mvn spotless:apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This will format your code according to the configured style&lt;/li&gt;
&lt;li&gt;Then the commit will proceed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Manual Spotless commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check for formatting issues without changing files: &lt;code&gt;mvn spotless:check&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Apply formatting to all files: &lt;code&gt;mvn spotless:apply&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 7: Customization Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Alternative Formatting Styles
&lt;/h3&gt;

&lt;p&gt;If you prefer the Eclipse formatter instead of Google's style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;java&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;eclipse&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;file&amp;gt;&lt;/span&gt;${project.basedir}/eclipse-formatter.xml&lt;span class="nt"&gt;&amp;lt;/file&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/eclipse&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to provide an Eclipse formatter configuration file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting Other File Types
&lt;/h3&gt;

&lt;p&gt;Add formatters for additional file types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;java&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Java configuration as before --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xml&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/main/**/*.xml&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;pom.xml&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/xml&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;yaml&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/main/resources/**/*.yml&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;src/main/resources/**/*.yaml&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/yaml&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Import Order
&lt;/h3&gt;

&lt;p&gt;Customize the import order to match your team's preferences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;importOrder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;order&amp;gt;&lt;/span&gt;com.yourcompany,com,org,javax,java,\#&lt;span class="nt"&gt;&amp;lt;/order&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/importOrder&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Different Pre-commit Actions
&lt;/h3&gt;

&lt;p&gt;To run both formatting check and tests in pre-commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;preCommit&amp;gt;&lt;/span&gt;mvn spotless:apply test&lt;span class="nt"&gt;&amp;lt;/preCommit&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pre-commit Hook Not Running
&lt;/h3&gt;

&lt;p&gt;If the pre-commit hook doesn't run automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check that the hook file has execution permissions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;chmod&lt;/span&gt; +x .git/hooks/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify Git hooks are not being ignored:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git config core.hooksPath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should return &lt;code&gt;.git/hooks&lt;/code&gt; or be empty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spotless Fails to Apply Formatting
&lt;/h3&gt;

&lt;p&gt;If Spotless fails with formatting errors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;mvn spotless:apply&lt;/code&gt; manually to see detailed error messages&lt;/li&gt;
&lt;li&gt;Check if your code has syntax errors that prevent formatting&lt;/li&gt;
&lt;li&gt;Ensure you're using a compatible Java version for the configured formatter&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Git Commit ID Plugin Fails
&lt;/h3&gt;

&lt;p&gt;If the Git Commit ID plugin fails:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure your project is a Git repository (&lt;code&gt;git init&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Make at least one initial commit before running the plugin&lt;/li&gt;
&lt;li&gt;Check if Git is properly installed and accessible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more advanced options and configurations, refer to the official documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/diffplug/spotless/tree/main/plugin-maven" rel="noopener noreferrer"&gt;Spotless Maven Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rudikershaw/git-build-hook" rel="noopener noreferrer"&gt;Git Build Hook Maven Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/git-commit-id/git-commit-id-maven-plugin" rel="noopener noreferrer"&gt;Git Commit ID Maven Plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
