<?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: Brandon Wozniewicz</title>
    <description>The latest articles on DEV Community by Brandon Wozniewicz (@scriptedbytes).</description>
    <link>https://dev.to/scriptedbytes</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%2F3195766%2Fce06a037-8eba-464d-9557-833262f55e28.png</url>
      <title>DEV Community: Brandon Wozniewicz</title>
      <link>https://dev.to/scriptedbytes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scriptedbytes"/>
    <language>en</language>
    <item>
      <title>What Power Apps Components Can Learn from Your Favorite Café</title>
      <dc:creator>Brandon Wozniewicz</dc:creator>
      <pubDate>Thu, 24 Jul 2025 15:00:00 +0000</pubDate>
      <link>https://dev.to/scriptedbytes/what-power-apps-components-can-learn-from-your-favorite-cafe-3ckk</link>
      <guid>https://dev.to/scriptedbytes/what-power-apps-components-can-learn-from-your-favorite-cafe-3ckk</guid>
      <description>&lt;p&gt;A good component is like your favorite coffee shop.&lt;/p&gt;

&lt;p&gt;You place your order.&lt;/p&gt;

&lt;p&gt;The barista prepares your drink.&lt;/p&gt;

&lt;p&gt;When it's ready, they call your name and hand it over — just the way you expected.&lt;/p&gt;

&lt;p&gt;And the best part? If you order the same drink tomorrow, it'll taste the same.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What a component is—in plain terms&lt;/li&gt;
&lt;li&gt;How inputs, outputs, and events relate to our coffee shop&lt;/li&gt;
&lt;li&gt;Three simple rules for building scalable components in power apps&lt;/li&gt;
&lt;li&gt;A real-world example of password matching&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is a Component?
&lt;/h2&gt;

&lt;p&gt;A component is a building block — one small part of something bigger.&lt;/p&gt;

&lt;p&gt;A LEGO brick is a component. Stack enough together, and you get a spaceship.&lt;/p&gt;

&lt;p&gt;Or take your car: it's made up of smaller components, such as the engine, wheels, and doors.&lt;/p&gt;

&lt;p&gt;Each part has a specific purpose, and when assembled, they form something useful.&lt;/p&gt;

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

&lt;p&gt;In programming, it's no different. A component is a reusable piece of code. Alone, it might render a button or a label. Together with other elements, it helps build entire applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau7qm8c2rgtrjyu6xx9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau7qm8c2rgtrjyu6xx9p.png" alt="Hand-drawn image of website and components" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How inputs, outputs, and events relate to our coffee shop
&lt;/h2&gt;

&lt;p&gt;Just like LEGO bricks or car parts, components often take inputs and produce outputs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inputs&lt;/strong&gt; flow into the component—instructions from the outside&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outputs&lt;/strong&gt; from out of the component— data or signals sent back to whoever's listening&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This relationship is often referred to as a &lt;strong&gt;parent-child&lt;/strong&gt; arrangement.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;parent&lt;/strong&gt; is the screen or component that "hosts" another component inside of it.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;child&lt;/strong&gt; is the reusable component that is placed inside the parent— like a coffee shop inside a larger shopping center.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The parent gives the child its settings (inputs).&lt;/p&gt;

&lt;p&gt;The child does its job and lets the parent know when something happens in the form of &lt;strong&gt;outputs and events&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Coffee Shop (Child Component) in Plain Terms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exists within a parent— the shopping center&lt;/li&gt;
&lt;li&gt;Inputs = Your order (e.g., "Oat milk latte, no foam)&lt;/li&gt;
&lt;li&gt;Logic = The barista behind the counter&lt;/li&gt;
&lt;li&gt;Event = "Order for Sam!" — a signal that something has happened.&lt;/li&gt;
&lt;li&gt;Output = Your drink&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the model: a clean component takes instructions, performs its job, and notifies you when it's done.&lt;/p&gt;

&lt;p&gt;But what does that actually &lt;em&gt;look&lt;/em&gt; like in Power Apps?&lt;/p&gt;

&lt;p&gt;Let’s walk through three simple rules that will help you design components that are easy to reuse, easy to test, and hard to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three simple rules for building scalable components in Power Apps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rule #1: Take the Order and Follow the Recipe
&lt;/h3&gt;

&lt;p&gt;A well-designed component is like a well-run coffee franchise— it should function &lt;strong&gt;the same way&lt;/strong&gt;, regardless of its location.&lt;/p&gt;

&lt;p&gt;It doesn't matter what's happening at the restaurant next door.&lt;/p&gt;

&lt;p&gt;It doesn't matter if it's the morning rush or a quiet afternoon.&lt;/p&gt;

&lt;p&gt;And it definitely doesn't matter which location you visit — you expect your usual drink to come out… well, as usual.&lt;/p&gt;

&lt;p&gt;Good components don't reach into the parent screen to grab what they need.&lt;/p&gt;

&lt;p&gt;They don't rely on global variables.&lt;/p&gt;

&lt;p&gt;And they don't make assumptions.&lt;/p&gt;

&lt;p&gt;If a component needs information — like a title, color, or mode — that's handled through &lt;strong&gt;explicit inputs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the parent needs information back, that is handled through &lt;strong&gt;explicit outputs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b2yjem99edw4puiruje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b2yjem99edw4puiruje.png" alt="Map of two identical coffee shop locations" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #2: Make the Same Order the Same Way Every Time
&lt;/h2&gt;

&lt;p&gt;When you order a #5, you always expect the same drink.&lt;/p&gt;

&lt;p&gt;It doesn't matter if there's a new barista behind the counter or they've upgraded the espresso machine — your oat milk latte should taste the same.&lt;/p&gt;

&lt;p&gt;Your components should behave similarly.&lt;/p&gt;

&lt;p&gt;In Power Apps, if a component receives an input like &lt;code&gt;Title&lt;/code&gt; = "Are you sure?", that same title should appear the same way every time — without surprises.&lt;/p&gt;

&lt;p&gt;It shouldn't sometimes include an emoji.&lt;/p&gt;

&lt;p&gt;Or occasionally add extra punctuation.&lt;/p&gt;

&lt;p&gt;While the parent provides the settings (inputs) to the component, how the component uses them to produce the output is up to the component.&lt;/p&gt;

&lt;p&gt;These are called &lt;strong&gt;implementation details&lt;/strong&gt;. And the parent component shouldn't have to care.&lt;/p&gt;

&lt;p&gt;You ordered a drink with a tablespoon of sugar.&lt;/p&gt;

&lt;p&gt;You don't care if they used a tablespoon or three teaspoons — as long as the drink tastes right.&lt;/p&gt;

&lt;p&gt;That's precisely what makes a component testable, reusable, and predictable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp13xz7sxue1xy343s8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp13xz7sxue1xy343s8q.png" alt="Image of functions that deal with implementation details" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #3: Announce When It's Ready
&lt;/h2&gt;

&lt;p&gt;Imagine the barista finishes your drink… and then leaves it on the counter.&lt;/p&gt;

&lt;p&gt;No name on the drink. No name called. No order number announced. No signal at all.&lt;/p&gt;

&lt;p&gt;You sit there wondering what's taking so long — while your drink is slowly getting cold.&lt;/p&gt;

&lt;p&gt;That's what happens when a component forgets to raise an event.&lt;/p&gt;

&lt;p&gt;In Power Apps, when something meaningful happens — such as a button click or a value selection — the component should &lt;strong&gt;notify its parent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To do this, the child component raises an event — such as &lt;code&gt;OnSubmit&lt;/code&gt;, &lt;code&gt;OnItemSelected&lt;/code&gt;, or &lt;code&gt;OnChange&lt;/code&gt; — to &lt;strong&gt;notify the parent that something important has just occurred&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A button was clicked.&lt;/li&gt;
&lt;li&gt;A choice was made.&lt;/li&gt;
&lt;li&gt;A step was completed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component raised its hand and said, "Hey — something's ready."&lt;/p&gt;

&lt;p&gt;The parent can then listen and react to these events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q3wdhy4rg58a6wsec40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q3wdhy4rg58a6wsec40.png" alt="Image of employee telling a customer their order is ready" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real-World Example: Password Matching
&lt;/h2&gt;

&lt;p&gt;It's time to apply these concepts with a real world example. Let's say you're building a form with two fields:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Password&lt;/li&gt;
&lt;li&gt;Verify Password&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foz1jn4ix4whymygg7np3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foz1jn4ix4whymygg7np3.png" alt="Image of traditional password-matching fields" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a suboptimal way to build it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have &lt;strong&gt;two separate input components&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each has a static label: "Password" and "Verify Password".&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;second component&lt;/strong&gt; checks a &lt;strong&gt;global variable&lt;/strong&gt; to get the value of the first input.&lt;/li&gt;
&lt;li&gt;It uses that global value to decide whether to show a message like: "&lt;em&gt;Passwords do not match&lt;/em&gt;"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem?&lt;/p&gt;

&lt;p&gt;This component &lt;strong&gt;isn't isolated&lt;/strong&gt; — it's reaching outside itself to gather what it needs. It only works because of the specific screen it's on. Move it somewhere else, and it breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Better Approach: Reusable and Self-Contained
&lt;/h3&gt;

&lt;p&gt;Let's improve this by building a &lt;strong&gt;single, reusable input component&lt;/strong&gt; that works for &lt;em&gt;both&lt;/em&gt; password fields.&lt;/p&gt;

&lt;p&gt;Here is how it works. This component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts a &lt;code&gt;LabelText&lt;/code&gt; input, like "Password" or "Verify Password," and displays it above the text input.&lt;/li&gt;
&lt;li&gt;Accepts a &lt;code&gt;HintText&lt;/code&gt; input, like "Passwords must match" or "Must include 1 special character", and displays it below the field.&lt;/li&gt;
&lt;li&gt;Exposes a &lt;code&gt;Value&lt;/code&gt; output—the current text input value.&lt;/li&gt;
&lt;li&gt;Raises an &lt;code&gt;OnChange&lt;/code&gt; event whenever the user types something new.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you can drop this component in any form: One for the password and one for the confirmation field.&lt;/p&gt;

&lt;p&gt;And, on the parent screen, you can handle the matching logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listen for &lt;code&gt;OnChange&lt;/code&gt; from either input.&lt;/li&gt;
&lt;li&gt;Compare their &lt;code&gt;Value&lt;/code&gt; outputs.&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;HintText&lt;/code&gt; of the second input accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A well-built component is like a well-run coffee shop — or, better yet, a coffee shop franchise.&lt;/p&gt;

&lt;p&gt;It waits for your order (input), prepares your drink behind the counter (logic), calls your name when it's ready (event), and hands you exactly what you expected (output).&lt;/p&gt;

&lt;p&gt;It doesn't guess, and it doesn't fall apart if you open a new location.&lt;/p&gt;

&lt;p&gt;By focusing on clear inputs, consistent outputs, and intentional events, you'll create components that are easy to reuse, easy to reason about, and ready to scale.&lt;/p&gt;

&lt;p&gt;If you want a practical look at canvas apps components, check out &lt;a href="https://www.scriptedbytes.com/posts/power-apps-reusable-components" rel="noopener noreferrer"&gt;this article.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Found this helpful?&lt;/strong&gt; I work at the intersection of low-code and pro-code development, focusing on building performant apps and helping you reclaim your time through thoughtful automation. Explore more right here at &lt;a href="https://scriptedbytes.com" rel="noopener noreferrer"&gt;scriptedbytes.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>How to Assign Dataverse Security Roles at Scale</title>
      <dc:creator>Brandon Wozniewicz</dc:creator>
      <pubDate>Thu, 26 Jun 2025 15:00:00 +0000</pubDate>
      <link>https://dev.to/scriptedbytes/how-to-assign-dataverse-security-roles-at-scale-2nkp</link>
      <guid>https://dev.to/scriptedbytes/how-to-assign-dataverse-security-roles-at-scale-2nkp</guid>
      <description>&lt;p&gt;Assigning Dataverse security roles manually works–until it doesn't. &lt;/p&gt;

&lt;p&gt;Whether you are onboarding 50 new hires or rolling out access to a new app, managing roles by hand can be tedious and error-prone. &lt;/p&gt;

&lt;p&gt;In this article you will learn &lt;strong&gt;three scalable ways&lt;/strong&gt; to assign security roles across multiple users or teams, with low-code and pro-code options.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Canvas Apps Relate/Unrelate Functions&lt;/li&gt;
&lt;li&gt;Power Automate and the Relate Rows Action&lt;/li&gt;
&lt;li&gt;C# Console App using the Dataverse SDK&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Aside: Teams
&lt;/h2&gt;

&lt;p&gt;Teams are the simplest way to assign roles to multiple users.&lt;/p&gt;

&lt;p&gt;Dataverse admins and team owners can add users to one or more teams. Rather than assigning roles to individual users, the security role is assigned to the team.&lt;/p&gt;

&lt;p&gt;However, there are caveats—record ownership and team management can introduce their own complexity. In environments with many teams, updating security roles can become just as tedious as assigning them individually.&lt;/p&gt;

&lt;p&gt;This article focuses on programmatic individual assignments, but keep in mind that all three methods described below can be adapted to work with teams as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canvas Apps Relate / Unrelate Functions
&lt;/h2&gt;

&lt;p&gt;Canvas app developers can use Power FX to assign security roles across users or teams. Minimal code is required, and the result can serve as a lightweight security role management portal.&lt;/p&gt;

&lt;p&gt;While not ideal for massive batches, this approach is suitable for assigning roles to a few hundred users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ForAll(
    colSelectedUsers As User,
    Relate(
        User.'Security Roles (systemuserroles_association)',
        cbx_securityRoles.Selected
    )
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;colSelectedUsers&lt;/code&gt; is a collection of users.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cbx_securityRoles&lt;/code&gt; is a Combobox control holding the security role to assign.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Relate&lt;/code&gt; function connects each user with the selected security role. The first parameter is the many-to-many relationship (&lt;code&gt;systemuserroles_association&lt;/code&gt;) between &lt;code&gt;systemuser&lt;/code&gt; and &lt;code&gt;role&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To find relationship names, open the User table &amp;gt; Relationships. Then, look for many-to-many connections to the role table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6jmnzmrhgawqzslba6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6jmnzmrhgawqzslba6j.png" alt="Image of how to find relationships in Dataverse." width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Power Automate with the Relate Rows in Dataverse Action
&lt;/h2&gt;

&lt;p&gt;The Relate Rows in Dataverse action allows you to assign roles dynamically in cloud flows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42700iocf8xvlq0qda11.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42700iocf8xvlq0qda11.jpg" alt="Image of cloud flow relating users and security roles." width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  How it works:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Trigger a flow (e.g., manually or via Dataverse trigger).&lt;/li&gt;
&lt;li&gt;Fetch a list of users based on a condition.&lt;/li&gt;
&lt;li&gt;Loop through each user with Apply to Each.&lt;/li&gt;
&lt;li&gt;Assign a static or dynamic security role.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  C# Console App: Using the Dataverse SDK
&lt;/h2&gt;

&lt;p&gt;This method offers maximum control and supports complex, high-scale role assignments—but it requires pro-code skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
This console app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connects to the environment via client credentials.&lt;/li&gt;
&lt;li&gt;Retrieves all users with the title "Salesperson"&lt;/li&gt;
&lt;li&gt;Builds a batch of associate requests.&lt;/li&gt;
&lt;li&gt;Executes the batch transactionally–if one fails, all fail.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;

class Program
{
    static void Main(string[] args)
    {
        string dataverseUrl = "https://your-org.crm.dynamics.com";
        string clientId     = "client-id-here";
        string clientSecret = "client-secret-here";
        string tenantId     = "tenant-id-here";

        string connectionString = $@"
            AuthType=ClientSecret;
            Url={dataverseUrl};
            ClientId={clientId};
            ClientSecret={clientSecret};
            TenantId={tenantId};
        ";
        // Connect to our environment
        using var serviceClient = new ServiceClient(connectionString);

        if (!serviceClient.IsReady)
        {
            Console.WriteLine("Failed to connect.");
            return;
        }

        // Fetch a list of users that we intend to associate a role with
        var query = new QueryExpression("systemuser")
        {
            ColumnSet = new ColumnSet("systemuserid"),
            Criteria = new FilterExpression
            {
                Conditions =
                {
                     new ConditionExpression(
                        "title",
                        ConditionOperator.Equal,
                        "Salesperson"
                     ),
                }
            }
        };

        var users = serviceClient.RetrieveMultiple(query);

        // Role to assign (pretend guid for demo purposes)
        var securityRoleId = new Guid(
            "00000000-0000-0000-0000-000000000ABC"
        );

        // Prepare our transaction
        var transaction = new ExecuteTransactionRequest
        {
            ReturnResponses = true,
            Requests = new OrganizationRequestCollection()
        };

        // For each user we fetched above, we add an associate request 
        // to the transaction
        foreach (var user in users.Entities)
        {
            var userId = (Guid)user["systemuserid"];

            var relationship = new Relationship(
                "systemuserroles_association"
            );

            var relatedReferences = new EntityReferenceCollection
            {
                new EntityReference(
                    "role",
                    securityRoleId
                )
            };
            // build the associate request
            var request = new AssociateRequest
            {
                Target = new EntityReference(
                    "systemuser",
                    userId
                ),
                RelatedEntities = relatedReferences,
                Relationship = relationship
            };
            // add the request to the transaction
            transaction.Requests.Add(request);
        }

        // Finally, execute the batch as a transaction
        serviceClient.Execute(transaction);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even utilize this logic within a Custom API, allowing Power Automate or Canvas Apps to call it, blending low-code and pro-code capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If your teams are already well-structured and manageable in number, teams remain the easiest way to assign roles at scale.&lt;/p&gt;

&lt;p&gt;However, when teams aren't feasible–or when assigning directly to users is required–each method discussed offers a viable alternative:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Canvas Apps&lt;/strong&gt; for lightweight, user-facing management portals&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Power Automate&lt;/strong&gt; when complexity is low and there is a need to trigger it in a variety of ways.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;C# and the Dataverse SDK&lt;/strong&gt; for full control and batch efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Ready to automate your role assignments? Start small—build a simple Power App or Flow—and scale your approach from there. Check out more tips and tricks at &lt;a href="https://scriptedbytes.com" rel="noopener noreferrer"&gt;ScriptedBytes.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.scriptedbytes.com/posts/dataverse-assign-security-roles" rel="noopener noreferrer"&gt;Scripted Bytes&lt;/a&gt;, where I write about scalable low-code development and app architecture.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>powerplatform</category>
      <category>powerapps</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Reduce Technical Debt in the Power Platform</title>
      <dc:creator>Brandon Wozniewicz</dc:creator>
      <pubDate>Sat, 14 Jun 2025 11:30:11 +0000</pubDate>
      <link>https://dev.to/scriptedbytes/how-to-reduce-technical-debt-in-the-power-platform-6de</link>
      <guid>https://dev.to/scriptedbytes/how-to-reduce-technical-debt-in-the-power-platform-6de</guid>
      <description>&lt;p&gt;&lt;strong&gt;Technical debt&lt;/strong&gt; refers to the future cost—measured in terms of time, money, effort, or opportunity—of choosing expedient solutions today instead of more deliberate and scalable ones. And it's not just a pro-code concept. &lt;/p&gt;

&lt;p&gt;It might be easier to understand if we compare it to financial debt.&lt;br&gt;
Howard G. Cunningham—the creator of the first wiki—described technical debt this way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Shipping first-time code is like going into debt. A little debt speeds&lt;br&gt;
development so long as it is paid back promptly with a rewrite. Objects&lt;br&gt;
make the cost of this transaction tolerable. The danger occurs when the &lt;br&gt;
debt is not repaid. Every minute spent on not-quite-right code counts as &lt;br&gt;
interest on that debt. Entire engineering organizations can be brought to &lt;br&gt;
a stand-still under the debt load of an unconsolidated implementation, &lt;br&gt;
object-oriented or otherwise.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, you'll learn why technical debt is just as much a concern in low-code projects as in traditional development—and why, in some ways, it can be even more prominent. We'll walk through eight common contributors to technical debt in Power Platform projects that, if left unchecked, can lead to future headaches.&lt;/p&gt;
&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Why Technical Debt Is Also a Low-Code Problem&lt;/li&gt;
&lt;li&gt;Hard-Coded or Static Values in Your Code&lt;/li&gt;
&lt;li&gt;Duplicated Code&lt;/li&gt;
&lt;li&gt;Poor Naming of Controls and Variables&lt;/li&gt;
&lt;li&gt;Overloaded Screens and Apps&lt;/li&gt;
&lt;li&gt;No Version Notes&lt;/li&gt;
&lt;li&gt;Invisible Logic&lt;/li&gt;
&lt;li&gt;Denormalized Data Models&lt;/li&gt;
&lt;li&gt;Leading with Layout Before Logic&lt;/li&gt;
&lt;li&gt;Wrapping Up&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Why Technical Debt Is Also a Low-Code Problem
&lt;/h1&gt;

&lt;p&gt;Technical debt builds when short-term decisions ignore long-term consequences. While it exists in any development, low-code platforms increase the risk for a simple reason: they remove much of the traditional friction that forces teams to slow down.&lt;/p&gt;

&lt;p&gt;With fewer barriers to entry, it's easier for citizen developers — and even professional developers new to the platform — to start building without considering maintainability, scalability, and security.&lt;/p&gt;

&lt;p&gt;Low-code platforms enable speed — but if you aren't intentional, the speed can create an environment that fosters the growth of technical debt.&lt;/p&gt;
&lt;h1&gt;
  
  
  Examples of Technical Debt in a Low-Code Project
&lt;/h1&gt;
&lt;h3&gt;
  
  
  Hard-Coded or Static Values in Your Code
&lt;/h3&gt;

&lt;p&gt;We've all seen code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Office365Outlook.SendEmailV2(
  gblAuthenticatedUser.Email, 
  "Sign-Up Request Received!", 
  "A team member will reach out shortly with more information."
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this appears to be fine. But what happens if the subject or body of the email needs to change?&lt;/p&gt;

&lt;p&gt;Hard-coded values are fragile. A better approach is to store your email templates in a data source, even if it only contains one record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;With({
  wthEmailTemplate: LookUp(EmailTemplates, TemplateType="new_signup")},
  Office365Outlook.SendEmailV2(
    gblAuthenticatedUser, 
    wthEmailTemplate.Subject, 
    wthEmailTemplate.Message
  )
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if the email needs to be changed, you update the data source, not the application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplicated Code
&lt;/h2&gt;

&lt;p&gt;While it can be trickier to avoid than in traditional code, duplicate logic is a future maintenance headache.&lt;/p&gt;

&lt;p&gt;Imagine two different ways to create comments in an application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;///Main dialog in a stream of comments
With(
    {
        wthNewlyCreatedComment: Patch(
            Comments,
            Defaults(Comments),
            {Comment: txt_hs_comment.Value}
        )
    },
    Collect(
        colComments,
        wthNewlyCreatedComment
    );
    Set(
        gblCommentCount,
        CountRows(colComments)
    )
)
//A different dialog when replying to another's comment
With(
    {
        wthNewlyCreatedComment: Patch(
            Comments,
            Defaults(Comments),
            {Comment: txt_hs_dialogComment.Value}
        )
    },
    Collect(
        colComments,
        wthNewlyCreatedComment
    );
    Set(
        gblCommentCount,
        CountRows(colComments)
    )
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two blocks do the same thing. If the logic ever changes, you must remember to update it in both places.&lt;/p&gt;

&lt;p&gt;A cleaner approach is to use a &lt;strong&gt;user-defined function (UDF)&lt;/strong&gt; to encapsulate logic that gets reused across your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App.Formulas
UpdateComments(comment: Text):Void = 
{
    With(
        {
            wthNewlyCreatedComment: Patch(
                Comments,
                Defaults(Comments),
                {Comment: comment}
            )
        },
        Collect(
            colComments,
            wthNewlyCreatedComment
        );
        Set(
            gblCommentCount,
            CountRows(colComments)
        )
    )
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then in each of the locations that need this formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Main dialog in a stream of comments
UpdateComments(txt_hs_comment.Value)

//A different dialog when replying to another's comment
UpdateComments(txt_hs_dialogComment.Value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of this writing, &lt;strong&gt;user-defined functions in canvas apps support side effects&lt;/strong&gt; (such as modifying collections or setting variables), but the overall feature is still in Preview.&lt;/p&gt;

&lt;p&gt;If UDFs aren't an option for your current use case, a common workaround is to use a hidden button that encapsulates the logic and call it with Select(ButtonName). Just keep in mind: the control must be on the same screen where it's being invoked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poor Naming of Controls and Variables
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Home Screen
  ButtonCanvas4
  TextCanvas2
  ButtonCanvas1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's wrong with the scenario above? It is impossible to know what each control is responsible for.&lt;/p&gt;

&lt;p&gt;Good naming isn't just a nice-to-have—it's one of the best ways to reduce confusion and improve maintainability, especially in collaborative environments.&lt;/p&gt;

&lt;p&gt;Here is an improved version that allows any developer to understand what these controls do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Home Screen
  txt_hs_userName
  btn_hs_submitForm
  btn_hs_cancelSubmission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we follow the pattern of:&lt;br&gt;
&lt;code&gt;[control_type]_[screen]_[control responsibility]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This helps make it easy to search for items quickly as well as identify what they do.&lt;/p&gt;

&lt;p&gt;Another aspect that naturally lends itself to naming conventions is the use of variables. Canvas apps have various methods for storing data locally. &lt;/p&gt;

&lt;p&gt;They include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collections (ClearCollect/Collect)&lt;/li&gt;
&lt;li&gt;Global Variables (Set)&lt;/li&gt;
&lt;li&gt;Local Variables (UpdateContext)&lt;/li&gt;
&lt;li&gt;Contextual Variables (With functions)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each type of variable has a different scoping associated with it. Collections are tables and available throughout your entire application. Global variables are also available throughout the entire application. Variables set using UpdateContext are scoped to the screen on which they are declared. And variables contained within a With function are available only within that function.&lt;/p&gt;

&lt;p&gt;It is a good idea to ensure that the variable name accurately reflects the type of variable it represents. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// prefixed with "wth" for a with-function scoped variable
With({wthNewlyCreatedUser: Patch(AppUsers,...)},...)

// prefixed with "ctx" for a screen-scoped contextual variable
UpdateContext({ctxCurrentPostVotes: LookUp(colPostVotes, ....)})

// prefixed with "gbl" for global
Set(gblAuthenticatedUser, LookUp(AppUsers,....))

// prefixed with "col" for a collection
ClearCollect(colUserRoles, LookUp(AppRoles, ...))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each data storage type is designated by a prefix that indicates its kind, which makes debugging an application easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overloaded Screens and Applications
&lt;/h2&gt;

&lt;p&gt;It can be tempting to keep everything on one screen for simple applications. However, canvas apps can quickly become non-performant if too many controls or too much logic is on a single screen. The recommended limit is no more than 500 controls per app and 300 controls per screen. Using and editing the application can slow down significantly if these limits are exceeded.&lt;/p&gt;

&lt;p&gt;One way to prevent this issue is to think more modularly. For example, you may have both administrative and non-administrative tasks within a single application. Instead, you can make two applications, one for admin users and the other for general users.&lt;/p&gt;

&lt;p&gt;Another way to avoid these issues in the same application is to build using components. The controls that make up a component don't count individually towards the screen limits and are also a natural way to reduce duplication within and across your applications. Components can be created within an application or as a component library (if your component needs to be used in multiple applications — for example, loaders/spinners and confirmation dialogs).&lt;/p&gt;

&lt;p&gt;For more information on components, refer to &lt;a href="https://www.scriptedbytes.com/posts/power-apps-reusable-components" rel="noopener noreferrer"&gt;this article&lt;/a&gt; I wrote about building reusable components.&lt;/p&gt;

&lt;h1&gt;
  
  
  No Version Notes
&lt;/h1&gt;

&lt;p&gt;As the Power Platform ecosystem grows, advanced versioning techniques are being introduced, including the integration of solutions with Git. But even if you don't have that git integration, there is something simple you can do.&lt;/p&gt;

&lt;p&gt;When you save an application after any non-trivial change, use the built-in version notes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgqn783hfg9yk1kym7x6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgqn783hfg9yk1kym7x6.jpg" alt="Image of saving version notes" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This simple habit will make two things much easier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you ever need to roll back changes, it becomes much easier to identify the correct version to roll back to.&lt;/li&gt;
&lt;li&gt;When using multiple environments (e.g., Dev, Test, and Prod), this can help you identify which version is currently in each environment, as the built-in version numbers may not necessarily match.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To view version notes for a canvas app, select 'View Details' for the app and then select the versions tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n0emul4e5s7g5rt71jt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n0emul4e5s7g5rt71jt.jpg" alt="Image of version notes" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Invisible Logic
&lt;/h2&gt;

&lt;p&gt;Invisible logic is logic that supports a product, but it is not immediately recognizable. For example, custom APIs and cloud flows can quickly become forgotten if there is no documentation reminding developers that these critical components exist — and what they actually do.&lt;/p&gt;

&lt;p&gt;One of the best ways to document a project is by using solutions. Solutions will typically include the majority of a project's assets—often more than 90%—but there are notable exceptions, such as SharePoint lists, Power BI reports, and certain external integrations. Some things a solution often won't or can't include are assets that belong to core or base solutions—for example, generic cloud flows that serve multiple projects or products. Depending on your solution strategy, you may not want to add these to each solution, and they will only exist in a core or base solution. Other things that fall under the umbrella of invisible logic include Power BI assets and Dataflows, along with their respective automation architectures (e.g., how and when a Dataflow gets triggered).&lt;/p&gt;

&lt;p&gt;As a best practice, utilize the self-documenting nature of solutions to provide references to all assets, logic, and dependencies that a project uses. Additionally, consider adopting a feature-based documentation practice, where each feature or user story implemented includes basic documentation, including high-level implementation details and any underlying logic. This could be a wiki-like document that allows developers, who may be troubleshooting or extending a feature, a simple way to get oriented before diving into an unfamiliar project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Denormalized Data Models
&lt;/h2&gt;

&lt;p&gt;Data normalization is a topic of its own, but you don't need to be an expert to get started building robust and scalable data models. In simple terms, data normalization involves grouping similar data elements and eliminating duplication.&lt;/p&gt;

&lt;p&gt;Take a look at the following example of the employee table.&lt;/p&gt;

&lt;p&gt;Employees Table (Denormalized)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Employee ID | Name   | Department Name | Department Location |
|-------------|--------|------------------|----------------------|
| 1           | Alice  | HR               | Building A           |
| 2           | Bob    | IT               | Building B           |
| 3           | Carol  | HR               | Building A           |
| 4           | Dan    | IT               | Building B           |
| 5           | Eve    | Finance          | Building C           |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above table, we can see the EMPLOYEE table records contain information about the department. Conceptually, this is fine, but the main issue is that the attributes of each record not only describe the employee but also provide details about the department. This type of data is referred to as denormalized data. Denormalized data makes the data model harder to scale and maintain. For example, if the &lt;code&gt;Department Name&lt;/code&gt; changes, we must locate every record with that department name and update it accordingly.&lt;/p&gt;

&lt;p&gt;Instead, let's examine a more normalized data model that consists of two tables now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Employees Table (Normalized)

| Employee ID | Name   | Department ID |
|-------------|--------|----------------|
| 1           | Alice  | 1              |
| 2           | Bob    | 2              |
| 3           | Carol  | 1              |
| 4           | Dan    | 2              |
| 5           | Eve    | 3              |


Departments Table

| Department ID | Department Name | Department Location |
|---------------|------------------|----------------------|
| 1             | HR               | Building A           |
| 2             | IT               | Building B           |
| 3             | Finance          | Building C           |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This data model eliminates duplication and simplifies attribute updates for the department, requiring only a single record update to be made. And because each attribute of the EMPLOYEES and DEPARTMENTS tables only describes the primary key of the respective table, this is a normalized data model.&lt;/p&gt;

&lt;p&gt;One common misconception among new developers is that more tables are a bad thing. Many believe that fewer data sources are easier to maintain, but that’s not always true. In development, what makes things easier to maintain isn't less of something, but rather how atomic, modular, and dependent-free it is. For example, a few small, pure functions that do just one thing will be easier to maintain than a single side-effect-producing function that does many things.&lt;/p&gt;

&lt;p&gt;Don't shy away from normalized data just because it creates more tables. Shy away from data models that won't scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One final note:&lt;/strong&gt; Denormalized data has its place, too, and it's not a bad thing. For example, reporting data is often denormalized and is much more preferred as it makes reporting logic much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leading with Layout Before Logic
&lt;/h2&gt;

&lt;p&gt;Low code makes it easy to jump in and start building, which is a significant benefit. But this model can also make it very easy to skip important aspects of development, such as requirement gathering, user interface design, and data modeling.&lt;/p&gt;

&lt;p&gt;It's perfectly fine to prototype ideas. This is great for quickly determining if something may or may not be feasible. However, you must have the discipline to stop before getting too far along, and take time to plan properly. &lt;/p&gt;

&lt;p&gt;For example, consider employing a business logic-first approach. This means that the requirements and core business logic are decided on (and often implemented) before you even start building the user interface. The core principle of this type of development is that, regardless of the interface a user chooses to interact with our data — and remember, a web application is nothing more than an interface to your data — the core business logic should function properly. In this light, a canvas app becomes just an aesthetic wrapper that complements what is hopefully well-designed business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Technical debt exists in both traditional and low-code development. Recognizing this debt early, before it begins to accumulate, is critical. Some tips that can reduce and keep technical debt at manageable levels are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Avoid hard-coded or static data in your app logic&lt;/li&gt;
&lt;li&gt;Eliminate duplicated logic with user-defined functions (UDFs)&lt;/li&gt;
&lt;li&gt;Use consistent naming conventions for controls and variables&lt;/li&gt;
&lt;li&gt;Break overloaded apps into multiple screens or multiple apps&lt;/li&gt;
&lt;li&gt;Add version notes to track meaningful changes&lt;/li&gt;
&lt;li&gt;Document invisible logic such as flows and APIs&lt;/li&gt;
&lt;li&gt;Normalize your data to reduce duplication&lt;/li&gt;
&lt;li&gt;Start with business logic—not layout or visuals&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Found this helpful?&lt;/strong&gt; I work at the intersection of low-code and pro-code development, focusing on building performant apps and helping you reclaim your time through thoughtful automation. Explore more at &lt;a href="https://scriptedbytes.com" rel="noopener noreferrer"&gt;scriptedbytes.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.scriptedbytes.com/posts/how-to-reduce-technical-debt-in-the-power-platform" rel="noopener noreferrer"&gt;Scripted Bytes&lt;/a&gt;, where I write about scalable low-code development and app architecture.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>powerapps</category>
      <category>powerplatform</category>
      <category>lowcode</category>
      <category>technicaldebt</category>
    </item>
    <item>
      <title>Using React 19 in Power Apps PCF Components</title>
      <dc:creator>Brandon Wozniewicz</dc:creator>
      <pubDate>Fri, 23 May 2025 18:38:50 +0000</pubDate>
      <link>https://dev.to/scriptedbytes/using-react-19-in-power-apps-pcf-components-1958</link>
      <guid>https://dev.to/scriptedbytes/using-react-19-in-power-apps-pcf-components-1958</guid>
      <description>&lt;p&gt;While working on a recent PCF (Power Apps Component Framework) project, I experimented with using &lt;strong&gt;React 19&lt;/strong&gt;—and it worked surprisingly well.&lt;/p&gt;

&lt;p&gt;If you're considering a similar setup, I wrote a post breaking down what the integration looked like, what worked, and what needed a few tweaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  In the article, I cover:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Setting up a React 19 + TypeScript environment inside a PCF control&lt;/li&gt;
&lt;li&gt;Handling JSX, props, and lifecycle events cleanly&lt;/li&gt;
&lt;li&gt;A small &lt;code&gt;tsconfig&lt;/code&gt; change that helps VS Code behave as expected&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://blog.scriptedbytes.com/react-19-in-power-apps-pcf/" rel="noopener noreferrer"&gt;Read the full tutorial on my blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If you're building custom components for Power Apps and want more control over structure and UI, this approach is worth exploring.&lt;/p&gt;

&lt;p&gt;I'd love to hear how others are structuring modern PCF projects—or what your component stack looks like.&lt;/p&gt;




&lt;p&gt;📬 I write at the intersection of low code and pro code over at &lt;a href="https://blog.scriptedbytes.com" rel="noopener noreferrer"&gt;Scripted Bytes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>powerapps</category>
      <category>lowcode</category>
      <category>react</category>
    </item>
  </channel>
</rss>
