" MicromOne

Pagine

How to Create a Hover Popup with an Embedded Game Using JavaScript

The technique can be useful in dashboards, CRM interfaces, internal tools, portals, and other web applications where you want to provide additional interactive content without changing the main page layout.


The basic idea is simple:

  1. Find a specific grid or control using a data-control-name attribute.

  2. Wait until the element becomes available in the DOM.

  3. Dynamically create a popup.

  4. Add an iframe containing external content.

  5. Display the popup when the mouse enters the grid.

  6. Hide it when the mouse leaves the grid and the popup is no longer being hovered.

For example, our target element can be identified using:

<div data-control-name="casetypecode">
    ...
</div>

The JavaScript function can then be called with:

disableAllGrids("casetypecode");

Waiting for the Grid to Become Available

One of the first challenges is that the target element may not exist immediately when the JavaScript code runs.

This is particularly common in applications that dynamically generate their interface.

The script solves this by using setInterval():

const interval = setInterval(function () {

    const parentDoc = window.parent.document;

    const grid = parentDoc.querySelector(
        '[data-control-name="' + dataControlName + '"]'
    );

    console.log("Looking for:", dataControlName);
    console.log("Grid:", grid);

    if (!grid) {
        return;
    }

    clearInterval(interval);

    // Continue with the setup...

}, 500);

Every 500 milliseconds, the script checks whether the requested element exists.

Once it finds the element, the interval is stopped with:

clearInterval(interval);

This prevents unnecessary DOM queries from continuing indefinitely.

Accessing the Parent Document

The script uses:

const parentDoc = window.parent.document;

This allows the code to work with the document containing the iframe rather than only the current document.

This technique requires the current page and its parent document to satisfy the browser's same-origin security rules. If they belong to different origins, browser security restrictions can prevent access to window.parent.document.

Creating the Popup Dynamically

Instead of requiring an existing popup in the HTML, the script creates one dynamically:

const popup = parentDoc.createElement("div");

We then configure its appearance using JavaScript:

popup.style.position = "fixed";
popup.style.width = "70vw";
popup.style.height = "70vh";
popup.style.backgroundColor = "#fff";
popup.style.border = "1px solid #ccc";
popup.style.borderRadius = "8px";
popup.style.boxShadow = "0 5px 20px rgba(0,0,0,.3)";
popup.style.zIndex = "999999";
popup.style.display = "none";
popup.style.overflow = "hidden";

A few properties are particularly important.

position: fixed

The popup is positioned relative to the browser viewport rather than the normal document flow.

z-index

popup.style.zIndex = "999999";

A high z-index helps ensure that the popup appears above other interface elements.

display: none

Initially, the popup is hidden:

popup.style.display = "none";

It becomes visible only when the user hovers over the target element.

Embedding External Content with an iframe

The next step is to create an iframe:

const iframe = parentDoc.createElement("iframe");

The iframe can then load the external game or application:

iframe.src = "YOUR_EXTERNAL_URL";

The iframe is configured to occupy the entire popup:

iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "0";

Finally, it is added to the popup:

popup.appendChild(iframe);

And the popup itself is added to the page:

parentDoc.body.appendChild(popup);

At this point, the popup exists in the DOM but remains invisible.

Showing the Popup on Mouse Hover

The popup is displayed when the user enters the grid:

grid.addEventListener("mouseenter", function () {

    const rect = grid.getBoundingClientRect();

    popup.style.left = rect.left + "px";
    popup.style.top = (rect.bottom + 10) + "px";

    popup.style.display = "block";
});

The key part is:

grid.getBoundingClientRect();

This returns the position and dimensions of the target element.

The script uses those coordinates to position the popup underneath the grid:

popup.style.left = rect.left + "px";
popup.style.top = (rect.bottom + 10) + "px";

The + 10 adds a small 10-pixel gap between the grid and the popup.

Hiding the Popup

When the mouse leaves the grid, we don't immediately hide the popup.

Instead, the script waits 100 milliseconds:

grid.addEventListener("mouseleave", function () {

    setTimeout(function () {

        if (!popup.matches(":hover")) {
            popup.style.display = "none";
        }

    }, 100);

});

This small delay is important.

Without it, moving the mouse from the grid toward the popup could cause the popup to disappear before the cursor reaches it.

The script checks:

popup.matches(":hover")

If the popup is not being hovered, it is hidden.

Closing the Popup When the Cursor Leaves It

There is also a second event handler:

popup.addEventListener("mouseleave", function () {
    popup.style.display = "none";
});

This ensures that the popup disappears as soon as the user moves the cursor away from it.

Together, the two mouse events create a simple hover interaction:

        Target Grid
             │
        mouse enters
             ↓
       ┌───────────┐
       │   Popup   │
       │           │
       │   iframe  │
       │           │
       └───────────┘
             │
       mouse leaves
             ↓
          Hidden

The Complete Concept

The complete function follows this sequence:

function disableAllGrids(dataControlName) {

    const interval = setInterval(function () {

        const parentDoc = window.parent.document;

        const grid = parentDoc.querySelector(
            '[data-control-name="' + dataControlName + '"]'
        );

        if (!grid) {
            return;
        }

        clearInterval(interval);

        const popup = parentDoc.createElement("div");

        popup.style.position = "fixed";
        popup.style.width = "70vw";
        popup.style.height = "70vh";
        popup.style.backgroundColor = "#fff";
        popup.style.border = "1px solid #ccc";
        popup.style.borderRadius = "8px";
        popup.style.boxShadow = "0 5px 20px rgba(0,0,0,.3)";
        popup.style.zIndex = "999999";
        popup.style.display = "none";
        popup.style.overflow = "hidden";

        const iframe = parentDoc.createElement("iframe");

        iframe.src = "YOUR_EXTERNAL_URL";

        iframe.style.width = "100%";
        iframe.style.height = "100%";
        iframe.style.border = "0";

        popup.appendChild(iframe);
        parentDoc.body.appendChild(popup);

        grid.addEventListener("mouseenter", function () {

            const rect = grid.getBoundingClientRect();

            popup.style.left = rect.left + "px";
            popup.style.top = (rect.bottom + 10) + "px";

            popup.style.display = "block";
        });

        grid.addEventListener("mouseleave", function () {

            setTimeout(function () {

                if (!popup.matches(":hover")) {
                    popup.style.display = "none";
                }

            }, 100);

        });

        popup.addEventListener("mouseleave", function () {
            popup.style.display = "none";
        });

    }, 500);
}

disableAllGrids("casetypecode");

Why Use a Function Parameter?

The function accepts:

dataControlName

This makes the solution reusable.

Instead of hard-coding:

'[data-control-name="casetypecode"]'

the script dynamically creates the selector:

'[data-control-name="' + dataControlName + '"]'

Therefore, you can target different controls simply by changing the function argument:

disableAllGrids("casetypecode");

or:

disableAllGrids("anotherControl");

This is much more flexible than creating a separate function for every grid.

Possible Improvements

Although the basic implementation works, there are several ways to make it more robust.

Use CSS Instead of Inline Styles

For larger projects, it is often cleaner to create a CSS class rather than setting every property with JavaScript.

For example:

.game-popup {
    position: fixed;
    width: 70vw;
    height: 70vh;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 5px 20px rgba(0, 0, 0, .3);
    z-index: 999999;
    overflow: hidden;
}

JavaScript can then simply use:

popup.className = "game-popup";

This makes the JavaScript easier to read and allows designers to modify the appearance without touching the code.

Add a Maximum Width and Height

Using:

width: 70vw;
height: 70vh;

works well for many screens, but the popup can become very large on high-resolution displays.

You could add limits such as:

max-width: 1200px;
max-height: 800px;

This can improve usability on larger monitors.

Consider Mobile Devices

Mouse hover does not exist in the same way on touch devices.

For mobile interfaces, a click or tap-based interaction is usually more appropriate.

For example, you could use:

grid.addEventListener("click", function () {
    // Open popup
});

and provide a dedicated close button.

Prevent Duplicate Popups

If the function is called multiple times for the same control, multiple event listeners and popups could potentially be created.

A production implementation should therefore consider storing a reference to the created popup or marking the grid as already initialized.

For example:

if (grid.dataset.popupInitialized === "true") {
    return;
}

grid.dataset.popupInitialized = "true";

This prevents the same element from being initialized more than once.

Browser Security Considerations

When embedding external content, browser security policies can affect whether the iframe is allowed to load.

The external website may use headers such as X-Frame-Options or Content Security Policy (frame-ancestors) to prevent embedding.

In addition, accessing:

window.parent.document

is subject to the browser's same-origin policy.

Therefore, this technique should always be tested in the actual environment where it will be deployed.

A dynamically generated hover popup is a simple but powerful way to add interactive content to an existing web interface.

The approach demonstrated here combines several useful JavaScript techniques:

  • DOM element detection

  • Dynamic element creation

  • iframe embedding

  • mouse event handling

  • viewport-based positioning

  • delayed hiding

  • reusable functions

  • interaction with a parent document


Understanding Database Normalization: Why SQL Databases Use Multiple Tables

When you first start learning SQL, one of the most confusing concepts can be understanding why information is divided across several tables instead of being stored in one large table.

At first glance, putting everything into a single table might seem easier. For example, you could store a customer's name, address, and all of their orders in the same place. However, relational databases are designed differently for some very important reasons.

One of the key concepts behind this structure is database normalization.

What Is Database Normalization?

Database normalization is the process of organizing data in a database so that information is stored efficiently, logically, and with as little unnecessary duplication as possible.

A well-designed database generally aims to achieve three important goals:

  1. Keep logically related information together.

  2. Allow information to be updated in one place whenever possible.

  3. Make it easier and more efficient to retrieve and manipulate data.

These principles help databases remain accurate, consistent, and efficient as the amount of data grows.

Why Use Multiple Tables?

Imagine a company such as Parch & Posey that needs to store information about its customers and their orders.

It might have an Accounts table containing information about each customer:

  • Account name

  • Street address

  • City

  • State

  • ZIP code

  • Country

It could then have a separate Orders table containing information about individual purchases.

Why not simply put all of this information into one table?

There are several reasons.

Different Types of Information

The first reason is that accounts and orders represent fundamentally different types of objects.

An account represents a customer or company. Usually, there is one account record for each customer, and the information associated with that account can change over time.

Orders are different. A customer can place many orders, and each order represents a specific transaction.

Once an order has been completed, its information generally should not change. If the customer places another order, the database can simply create another order record.

This difference in how the information behaves makes it logical to store accounts and orders in separate tables.

Avoiding Duplicate Data

Another major advantage of normalization is reducing unnecessary duplication.

Suppose the customer's name and complete address were stored in every order record.

If a customer had placed 100 orders, their address might appear 100 times in the database.

Now imagine that the customer moves to a new address.

Without a normalized structure, the database would potentially need to update the address on all 100 orders.

That means changing the same information repeatedly.

With a separate Accounts table, the customer's address only needs to be changed once.

The Orders table can simply reference the appropriate account.

This becomes increasingly important as databases grow. A company with thousands or millions of orders could otherwise have an enormous amount of duplicated information.

Why Database Structure Can Affect Performance

Database design can also influence query performance.

When a database executes a query, it needs to read data and perform whatever calculations are necessary to produce the requested result.

If the same information is unnecessarily repeated across a huge table, queries and updates can become more expensive.

By separating information into logical tables, a database can avoid storing the same data repeatedly and can work with more manageable sets of information.

Good database design therefore isn't only about keeping information organized. It can also contribute to efficient data retrieval and maintenance.

How Are Tables Connected?

If account information and order information are stored in different tables, how can we combine them when we need to analyze the data?

This is where one of the most important concepts in SQL comes into play:

JOINs.

A JOIN allows us to combine related information from two or more tables.

For example, an Orders table might contain an account identifier that tells us which customer placed each order. The Accounts table contains the corresponding customer information.

Using a JOIN, we can bring these pieces of information together in a query.

Conceptually, the database might contain something like this:

Accounts

Account IDAccount NameCityCountry
101Parch & PoseyNew YorkUSA
102Example CompanyLondonUK

Orders

Order IDAccount IDOrder Amount
5001101$1,200
5002101$850
5003102$2,100

The Account ID connects the two tables.

A SQL JOIN can then be used to answer questions such as:

  • Which company placed each order?

  • How much has each customer ordered?

  • How many orders has each account made?

  • What is the total revenue generated by each customer?

Why Normalization Matters

Normalization is an important part of database design because it helps prevent common problems caused by duplicated or poorly organized information.

A poorly designed database can create several issues:

  • Data redundancy: The same information is stored multiple times.

  • Update problems: A change may need to be made in many places.

  • Inconsistent data: Different records may contain different versions of the same information.

  • Storage inefficiency: Repeated information consumes unnecessary space.

  • Maintenance difficulties: The larger the database becomes, the harder it is to manage duplicated data.

A normalized database helps reduce these problems by placing information where it logically belongs.

Do Data Analysts Need to Design Databases?

If you are learning SQL as a data analyst, you may be wondering whether you need to become an expert in database normalization.

Usually, you don't need to design the database yourself.

In many analytical roles, the database has already been created by database administrators, data engineers, or developers. Your job is generally to understand how the tables are structured and use SQL to retrieve the information you need.

However, understanding normalization is still valuable.

It helps you understand why data is divided into multiple tables and, more importantly, how those tables are related.

Once you understand this structure, concepts such as primary keys, foreign keys, and JOINs become much easier to understand.

Relational databases are divided into multiple tables for good reasons.

Accounts and orders represent different types of information and often behave differently. Separating them reduces unnecessary duplication, makes updates easier, and can help databases operate more efficiently.

The key idea is not simply that databases contain multiple tables. The important point is that each table should have a logical purpose, and related tables can be connected when necessary.

For anyone learning SQL, this leads to one of the most important skills you can develop: understanding how tables relate to one another and using JOINs to bring the right information together.

Once you understand this concept, working with relational databases becomes much more intuitive—and writing useful SQL queries becomes significantly easier.

Detecting File Types from Base64 in C#: Prefix Matching vs Binary Inspection

When developing enterprise integrations, document management systems, Dynamics 365 plugins, or REST APIs, it is common to receive files as Base64 strings.

The challenge is determining the correct file extension and MIME type when the original metadata is missing or unreliable.

Recently, while troubleshooting a document download issue in a Dynamics 365 plugin, I compared two different approaches:

  • Version A: Detect files using Base64 prefixes
  • Version B: Detect files using binary signatures (magic bytes) and ZIP inspection

At first glance, both approaches seem valid. However, modern Office documents introduce challenges that make one solution significantly more reliable than the other.

The Real Problem

Suppose an external service returns a document as a Base64 string:


string retrieveResponse = GetDocumentFromExternalService();

The original filename might be incorrect:


Filename: report.csv
Content-Type: application/octet-stream

while the actual content could be:

  • PDF
  • PNG
  • JPEG
  • DOCX
  • XLSX
  • PPTX
  • ZIP
  • Legacy Microsoft Office Document

The application must inspect the content itself and determine the correct type.

Version A: Detecting Files Using Base64 Prefixes

The simplest approach consists of comparing the beginning of the Base64 string against a predefined dictionary of known signatures.


private readonly Dictionary<string,
(string Extension, string MimeType)> fileSignatures =
    new Dictionary<string,
        (string Extension, string MimeType)>
{
    { "iVBORw0KGgo",
        (".png", "image/png") },

    { "/9j/",
        (".jpg", "image/jpeg") },

    { "R0lGODdh",
        (".gif", "image/gif") },

    { "JVBER",
        (".pdf", "application/pdf") },

    { "UEsDBBQABgAIAAAAIQDfpNJsWg",
        (".docx",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document") },

    { "UEsDBBQABgAIAAAAIQBi7p1oXgE",
        (".xlsx",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") },

    { "UEsDBBQABgAIAAAAIQDfzBj1rQE",
        (".pptx",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation") },

    { "UEsDB",
        (".zip", "application/zip") }
};

Detection is straightforward:


foreach (var signature in fileSignatures)
{
    if (base64.StartsWith(signature.Key))
    {
        return signature.Value;
    }
}

Advantages

  • Very simple
  • Easy to extend
  • Fast string comparisons
  • No ZIP processing

Disadvantages

  • Relies on specific sample files
  • Not reliable for OpenXML documents
  • Difficult to maintain
  • May fail with files created by different software

Why Base64 Prefix Matching Is Fragile

Modern Microsoft Office formats are actually ZIP containers.

  • .docx
  • .xlsx
  • .pptx

All of them typically begin with the ZIP signature:


50 4B 03 04

which becomes:


UEsDB

when converted to Base64.

The problem is that everything following the ZIP header depends on:

  • ZIP entry ordering
  • Metadata
  • Timestamps
  • Compression flags
  • Archive generation tool

Consider these real examples:


DOCX:
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAAT...

XLSX:
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAAT...

PPTX:
UEsDBBQABgAIAAAAIQDfzBj1rQEAAEYMAAAT...

ZIP:
UEsDBBQAAAAIACt+I10vkupli2EAAJuCAAAL...

These values identify specific files, not the Office formats themselves.

A different XLSX document generated by another application may have a completely different prefix.

Key takeaway:
Long Base64 prefixes are not universal file signatures. They are characteristics of individual files.

Version B: Binary Signature Detection

A significantly more reliable solution is:

  1. Decode the Base64 content
  2. Inspect actual binary signatures
  3. Analyze ZIP structure when necessary

Step 1: Convert Base64 to Bytes


byte[] bytes =
    Convert.FromBase64String(base64);

Step II: Detect Magic Numbers

PDF


if (StartsWith(
    bytes,
    0x25, 0x50, 0x44, 0x46))
{
    return ".pdf";
}

PNG


if (StartsWith(
    bytes,
    0x89, 0x50, 0x4E, 0x47,
    0x0D, 0x0A, 0x1A, 0x0A))
{
    return ".png";
}

JPEG


if (StartsWith(
    bytes,
    0xFF, 0xD8, 0xFF))
{
    return ".jpg";
}

Notice that we only verify:


FF D8 FF

instead of:


FF D8 FF E0

which supports additional JPEG variants such as EXIF files.

Handling DOCX, XLSX and PPTX Correctly

Magic bytes alone cannot distinguish Office Open XML formats because they all begin as ZIP archives.

The solution is inspecting the package structure:


using var stream =
    new MemoryStream(bytes);

using var archive =
    new ZipArchive(
        stream,
        ZipArchiveMode.Read);

Then inspect the internal folders:


if (archive.Entries.Any(
    e => e.FullName.StartsWith("word/")))
{
    return ".docx";
}

if (archive.Entries.Any(
    e => e.FullName.StartsWith("xl/")))
{
    return ".xlsx";
}

if (archive.Entries.Any(
    e => e.FullName.StartsWith("ppt/")))
{
    return ".pptx";
}

If none of those folders exist:


return ".zip";

This approach remains valid regardless of:

  • Timestamps
  • Metadata
  • Compression level
  • ZIP ordering
  • Generating software

Performance Comparison

AspectVersion AVersion B
SpeedVery FastFast
Memory UsageLowModerate
Detect PDF/ImagesGoodExcellent
Detect DOCX/XLSX/PPTXUnreliableReliable
MaintainabilityDifficultExcellent
Production ReadyNoYes

Security Considerations

During troubleshooting it may be tempting to log the entire Base64 content:


tracingService.Trace(retrieveResponse);

This is usually a bad idea because:

  • Logs may truncate the content
  • Large files produce huge traces
  • Sensitive information could be exposed
  • Performance may degrade

A safer approach is logging only the first bytes:


string header =
    BitConverter.ToString(
        bytes,
        0,
        Math.Min(16, bytes.Length));

tracingService.Trace(
    $"Header: {header}");

Final Verdict

After comparing both implementations, the conclusion is clear.

Version A

  • Simple
  • Fast
  • Suitable for demonstrations
  • Not reliable for modern Office documents

Version B

  • Uses real binary signatures
  • Handles OpenXML documents correctly
  • Returns accurate MIME types
  • Production-ready
  • Much more robust


How to Disable Subgrids in Microsoft Dynamics 365 Using JavaScript

When working with Microsoft Dynamics 365, there are situations where we want users to see the records displayed in a subgrid but prevent them from interacting with the grid.

For example, a form may contain the following subgrids:

  • Activities

  • Marketing Lists

  • Contacts

  • Opportunities

  • Custom related records

Sometimes the business requirement is not to hide these grids, but simply to make them read-only from the user interface.

In this article, we will look at a JavaScript approach that disables user interaction with specific Dynamics 365 subgrids.

The basic approach

The idea is simple:

  1. Execute JavaScript when the form loads.

  2. Retrieve the Dynamics 365 formContext.

  3. Load the application's utility functions.

  4. Locate the required subgrid in the DOM.

  5. Disable mouse interaction with the grid.

The following example disables the Activities and Marketing Lists grids:

utilities.disableAllGrids("Activities");
utilities.disableAllGrids("MarketingLists");

The grid names passed to the function correspond to the data-control-name attribute used by the Dynamics 365 interface.

Form OnLoad JavaScript

A typical form OnLoad function can look like this:

'use strict';

if (typeof (DPS) === "undefined") {
    var DPS = {};
}

if (typeof (DPS.Main) === "undefined") {
    DPS.Main = {
        __namespace: true
    };
}

DPS.Main = new function () {

    var _self = this;
    var _formContext = null;
    var _executionContext = null;
    var utilities = null;

    _self.OnLoad = async function (executionContext) {

        if (executionContext) {
            _formContext = executionContext.getFormContext();
            _executionContext = executionContext;
        }

        utilities = DPS.utilities;

        utilities.load(_formContext);

        utilities.disableAllGrids("Activities");
        utilities.disableAllGrids("MarketingLists");
    };
};

The executionContext is important because it allows us to obtain the current form context:

_formContext = executionContext.getFormContext();

Using getFormContext() is preferable to relying on older APIs such as Xrm.Page.

Creating a reusable utility function

Instead of writing separate code for every subgrid, we can create a reusable function.

For example:

'use strict';

if (typeof (DPS) === "undefined") {
    var DPS = {};
}

if (typeof DPS.utilities === "undefined") {
    DPS.utilities = {
        __namespace: true
    };
}

DPS.utilities = new function () {

    var _self = this;

    _self.disableAllGrids = function (dataControlName) {

        var interval = setInterval(function () {

            const grid = window.parent.document.querySelector(
                '[data-control-name="' + dataControlName + '"]'
            );

            if (grid) {

                grid.style.pointerEvents = "none";
                grid.style.opacity = "0.8";
                grid.style.cursor = "not-allowed";

                clearInterval(interval);
            }

        }, 500);
    };
};

The function accepts the name of the grid:

disableAllGrids("Activities");

or:

disableAllGrids("MarketingLists");

This makes the function reusable for multiple subgrids.

Why use setInterval()?

One of the challenges with Dynamics 365 subgrids is that they are not necessarily available in the DOM immediately when the form's OnLoad event executes.

The form can load first while the subgrid is rendered a little later.

If we execute:

document.querySelector(...)

immediately, the result may be null.

For this reason, the example uses:

var interval = setInterval(function () {
    ...
}, 500);

The function checks every 500 milliseconds until the grid becomes available.

Once the grid is found, the interval is stopped:

clearInterval(interval);

This prevents unnecessary repeated execution.

Locating the subgrid

The key part of the function is:

const grid = window.parent.document.querySelector(
    '[data-control-name="' + dataControlName + '"]'
);

For example, when:

dataControlName = "Activities";

the selector becomes:

[data-control-name="Activities"]

querySelector() then attempts to find the corresponding HTML element.

Once the element is found, we can manipulate its CSS properties.

Preventing user interaction

The most important line is:

grid.style.pointerEvents = "none";

CSS pointer-events: none prevents mouse interaction with the element.

The example also changes the appearance of the grid:

grid.style.opacity = "0.8";
grid.style.cursor = "not-allowed";

The result is a visual indication that the grid is not intended to be interactive.

Conceptually, the user sees the records but cannot click the grid controls.

Complete example

The following is a simplified complete implementation.

Main.js

'use strict';

if (typeof DPS === "undefined") {
    var DPS = {};
}

if (typeof DPS.Main === "undefined") {
    DPS.Main = {
        __namespace: true
    };
}

DPS.Main = new function () {

    var _self = this;
    var _formContext = null;

    _self.OnLoad = async function (executionContext) {

        if (executionContext) {
            _formContext = executionContext.getFormContext();
        }

        var utilities = DPS.utilities;

        utilities.load(_formContext);

        utilities.disableAllGrids("Activities");
        utilities.disableAllGrids("MarketingLists");
    };
};

Utilities.js

'use strict';

if (typeof DPS === "undefined") {
    var DPS = {};
}

if (typeof DPS.utilities === "undefined") {
    DPS.utilities = {
        __namespace: true
    };
}

DPS.utilities = new function () {

    var _self = this;

    _self.disableAllGrids = function (dataControlName) {

        var interval = setInterval(function () {

            var grid = window.parent.document.querySelector(
                '[data-control-name="' + dataControlName + '"]'
            );

            if (grid) {

                grid.style.pointerEvents = "none";
                grid.style.opacity = "0.8";
                grid.style.cursor = "not-allowed";

                clearInterval(interval);
            }

        }, 500);
    };
};

Is this a true security solution?

This is an important distinction.

Setting:

pointerEvents = "none";

only prevents interaction with the user interface. It does not change the user's Dynamics 365 security privileges.

If a user has permission to create, update, or delete records, JavaScript should not be considered a security mechanism for preventing those operations.

For real security requirements, use the appropriate Dataverse security roles, privileges, business rules, server-side validation, or other supported platform mechanisms.

The JavaScript approach is primarily a UI/UX solution.

DOM manipulation and Dynamics 365

There is another consideration with this technique.

The code accesses:

window.parent.document

and searches for:

[data-control-name="..."]

This means it depends on the current HTML implementation of the Dynamics 365 interface.

Microsoft can change internal DOM structures and CSS classes as the platform evolves. Therefore, DOM manipulation is generally more fragile than using officially supported client APIs.

For this reason, this technique should be used carefully and tested after Dynamics 365 updates.

When is this approach useful?

This technique can be useful when the requirement is essentially:

"The user should be able to see the related records, but should not be able to interact with this subgrid."

Typical scenarios include:

  • Showing Activities without allowing interaction.

  • Displaying related Marketing Lists as informational data.

  • Preventing users from opening grid commands.

  • Creating a UI that behaves differently depending on the record state.

  • Temporarily preventing interaction while another operation is taking place.

For example, the function can easily be reused:

utilities.disableAllGrids("Contacts");
utilities.disableAllGrids("Opportunities");
utilities.disableAllGrids("Activities");



Disabling Dynamics 365 subgrids with JavaScript can be useful when a business process requires a read-only visual representation of related records without hiding the subgrid completely.

The main technique is to wait until the grid is rendered, locate it using its data-control-name, and disable pointer interaction:

grid.style.pointerEvents = "none";

However, it is important to remember that this is a client-side UI technique, not a security mechanism.

For production implementations, always consider whether the requirement can be achieved through supported Dynamics 365 and Dataverse APIs or security configuration before relying on direct DOM manipulation.

REST vs. SOAP: A Practical Guide to Choosing the Right API Architecture

When building or integrating web services, one of the most common decisions developers face is whether to use REST or SOAP. Both approaches allow applications to communicate over a network, but they differ significantly in design, data formats, flexibility, and typical use cases.

In this article, we’ll compare REST and SOAP, look at practical code examples, and explain when each approach makes the most sense.

What Is REST?

REST stands for Representational State Transfer. It is an architectural style for designing web services around resources.

In a REST API, resources are identified by URLs, and standard HTTP methods describe the actions performed on them:

  • GET — Retrieve a resource

  • POST — Create a resource

  • PUT — Replace a resource

  • PATCH — Partially update a resource

  • DELETE — Remove a resource

REST APIs commonly use JSON because it is lightweight and easy for both humans and applications to read.

REST Example

Suppose we want to retrieve a user with ID 42.

Request:

GET /api/users/42 HTTP/1.1
Host: example.com
Accept: application/json

Response:

{
  "id": 42,
  "name": "Mario Rossi",
  "email": "mario@example.com"
}

The URL identifies the resource, while the HTTP method indicates the operation.

REST with JavaScript

Here is a simple example using the browser’s fetch() API:

fetch("https://example.com/api/users/42")
  .then(response => {
    if (!response.ok) {
      throw new Error("Request failed");
    }
    return response.json();
  })
  .then(user => {
    console.log(user.name);
  })
  .catch(error => {
    console.error(error);
  });

This example sends an HTTP request, parses the JSON response, and displays the user’s name.

What Is SOAP?

SOAP stands for Simple Object Access Protocol. Unlike REST, SOAP is a formal messaging protocol with a defined XML-based message structure.

SOAP messages are wrapped in an Envelope and contain a Body. They can also include headers for additional information, such as authentication or transaction-related data.

SOAP services are often described using WSDL (Web Services Description Language), which defines the available operations, messages, and data types.

SOAP Example

Let’s retrieve the same user using a SOAP operation called GetUser.

Request:

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUser>
      <UserId>42</UserId>
    </GetUser>
  </soap:Body>
</soap:Envelope>

Response:

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUserResponse>
      <User>
        <Id>42</Id>
        <Name>Mario Rossi</Name>
        <Email>mario@example.com</Email>
      </User>
    </GetUserResponse>
  </soap:Body>
</soap:Envelope>

SOAP uses XML for both requests and responses, which makes the messages more verbose than typical REST responses.

REST vs. SOAP: Key Differences

1. Architecture vs. Protocol

REST is an architectural style based on principles such as statelessness, resource-oriented design, and the use of standard HTTP methods.

SOAP is a protocol with a formal message structure and a collection of related standards.

This distinction is important: REST is not a protocol, and SOAP is not simply “REST with XML.”

2. Data Format

REST can technically use different formats, including JSON, XML, and plain text. However, JSON is the most common choice for modern REST APIs.

SOAP uses XML as its standard message format.

{
  "status": "success"
}

The equivalent SOAP-style message is more structured:

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Response>
      <Status>success</Status>
    </Response>
  </soap:Body>
</soap:Envelope>

3. Communication Style

REST typically uses HTTP verbs to represent operations on resources.

GET    /users/42
POST   /users
PUT    /users/42
DELETE /users/42

SOAP usually exposes operations through messages.

<GetUser>
  <UserId>42</UserId>
</GetUser>

REST focuses on resources, while SOAP focuses on operations.

4. Performance and Overhead

REST APIs are often easier to consume because JSON messages are generally smaller and require less processing than SOAP XML envelopes.

SOAP messages can be larger because of their XML structure and additional protocol information.

However, performance depends on many factors, including:

  • Payload size

  • Network latency

  • Serialization and parsing

  • Server implementation

  • Caching

  • Authentication mechanisms

REST is not automatically faster in every situation, but it is often a practical choice for lightweight web and mobile applications.

5. Security

Both REST and SOAP can be secured.

REST commonly relies on:

  • HTTPS

  • OAuth 2.0

  • OpenID Connect

  • JWT-based authentication

SOAP can use HTTPS as well, but it also supports standards such as WS-Security, which can provide message-level security features.

For example, SOAP can include security information inside the message itself, while REST applications often rely on transport security and application-level authentication.

6. Contract and Documentation

REST APIs are commonly documented using OpenAPI.

A simplified OpenAPI example:

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0

paths:
  /users/{id}:
    get:
      summary: Get a user
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Successful response

SOAP services commonly use WSDL, which provides a formal description of the service contract.

This can be especially useful in enterprise environments where strict contracts and generated client code are important.

REST vs. SOAP: Comparison Table

FeatureRESTSOAP
TypeArchitectural styleProtocol
Common formatJSONXML
CommunicationHTTP methods and resourcesXML messages and operations
ContractOpenAPI or similarWSDL
ComplexityUsually simplerUsually more complex
Message sizeOften smallerOften larger
CachingNaturally supported through HTTPMore complicated
SecurityHTTPS, OAuth, JWT, etc.HTTPS, WS-Security, etc.
Typical use casesWeb apps, mobile apps, microservicesEnterprise integrations, legacy systems
Learning curveGenerally lowerGenerally higher

When Should You Choose REST?

REST is usually a strong choice when you are building:

  • A public API

  • A mobile application backend

  • A web application

  • A microservices architecture

  • A lightweight integration between modern systems

For example, a shopping application might expose endpoints such as:

GET    /products
GET    /products/123
POST   /orders
DELETE /cart/items/123

This resource-oriented structure is easy to understand and works well with standard HTTP tooling.

When Should You Choose SOAP?

SOAP can be the better choice when you need:

  • A formal service contract

  • Integration with existing enterprise systems

  • Compatibility with legacy platforms

  • Advanced WS-* standards

  • Message-level security requirements

  • Enterprise features such as standardized transactions

For example, a banking or insurance system may already expose SOAP services that must be consumed by other applications.

In such cases, using SOAP may be more practical than replacing an established integration.

Can REST and SOAP Work Together?

Yes. REST and SOAP are not mutually exclusive.

A modern application might expose a REST API to mobile clients while communicating with an internal SOAP service.

For example:

Mobile App
    |
    v
REST API
    |
    v
Integration Layer
    |
    v
SOAP Enterprise Service

The integration layer translates REST requests into SOAP messages and converts SOAP responses back into JSON.

This approach allows modern applications to work with legacy systems without requiring a complete rewrite.

REST and SOAP solve similar problems, but they are designed with different priorities.

REST is generally simpler, flexible, and well suited to modern web applications and APIs.

SOAP is more formal and structured, making it valuable for enterprise integrations and systems that depend on established standards.

The best choice depends on your project’s requirements—not simply on which technology is newer.

If you are designing a new API, REST is often a good starting point. If you are integrating with an existing enterprise platform, SOAP may be the right tool for the job.

The goal is not to choose the most popular technology, but the one that best fits your system.

How to Create an Effective Staff Meeting Agenda

 Staff meetings can be one of the most valuable ways to keep a team aligned—or one of the biggest drains on productivity.

When meetings lack structure, conversations can easily go off track, important issues get overlooked, and team members leave without knowing what they are expected to do next.

A well-designed staff meeting agenda changes that. It gives the conversation a clear direction, helps participants prepare, and makes it easier to turn discussions into decisions and actions.

In this guide, we'll explain how to create a staff meeting agenda that keeps your team focused, engaged, and accountable.

What Is a Staff Meeting Agenda?

A staff meeting agenda is a structured plan that outlines what a team will discuss during a meeting.

A good agenda typically includes:

  • The purpose of the meeting

  • Key topics for discussion

  • Important updates

  • Project or goal progress

  • Current challenges and blockers

  • Decisions that need to be made

  • Action items and next steps

The goal isn't to control every minute of the meeting. Instead, an agenda provides a framework that helps everyone understand why the meeting is happening and what should be accomplished.

Why Is a Staff Meeting Agenda Important?

Without an agenda, staff meetings can quickly become a collection of unrelated updates and spontaneous discussions.

A clear agenda helps your team:

Keep everyone aligned

Team members can see the most important priorities and understand how their work connects to broader objectives.

Make better use of meeting time

When topics and time allocations are defined in advance, it's easier to prevent discussions from taking over the entire meeting.

Identify problems earlier

A dedicated section for blockers gives employees an opportunity to raise issues before they become larger problems.

Encourage participation

Sharing the agenda ahead of time gives everyone an opportunity to contribute questions and topics—not just the most vocal people in the room.

Create accountability

Recording decisions and assigning specific owners to follow-up tasks makes it much easier to ensure that commitments are actually completed.

What Should You Include in a Staff Meeting Agenda?

There is no single perfect format for every team. However, the following sections provide a strong starting point.

1. Meeting Purpose

Start by explaining why the meeting is taking place.

Try to define the purpose in one or two sentences. For example:

"Review progress toward this quarter's goals, identify major blockers, and agree on priorities for the coming week."

A clear purpose helps participants distinguish between topics that belong in the meeting and issues that can be handled elsewhere.

2. Attendees

List the people who need to participate.

Avoid inviting people simply because they might be interested in the conversation. If someone doesn't need to contribute to the discussion or make a decision, they may be better served by receiving a summary afterward.

3. Team Updates

Use this section for important information that everyone needs to know.

Depending on your organization, this could include:

  • Company announcements

  • Changes in priorities

  • New projects

  • Staffing updates

  • Customer or business developments

  • Upcoming deadlines

Keep routine announcements concise. A staff meeting should not become a long series of presentations.

4. Progress and Priorities

Review the team's most important objectives and current projects.

Rather than asking every person to provide a lengthy status report, focus on information that requires team awareness, discussion, or action.

Useful questions include:

  • What has changed since the last meeting?

  • Are we on track?

  • Which priorities require additional attention?

  • Are there dependencies between team members or projects?

5. Blockers and Challenges

Give the team a dedicated opportunity to discuss obstacles.

For example:

  • A project is waiting for approval.

  • A deadline is at risk.

  • A customer issue requires additional resources.

  • Two teams have conflicting priorities.

  • A technical or operational problem is slowing progress.

The objective isn't simply to list problems. Whenever possible, use this part of the meeting to agree on solutions and identify who will take the next step.

6. Decisions and Discussion Topics

Not every issue needs to be discussed by the entire team.

For topics that do require group input, clearly state what decision or outcome is expected.

Instead of writing:

"Marketing campaign"

write:

"Decide whether to launch the campaign in October or November."

The second version makes it much easier for participants to prepare.

7. Recognition and Wins

Don't make every staff meeting about problems and deadlines.

Take a few minutes to recognize achievements, celebrate progress, and thank team members for their contributions.

This can be as simple as highlighting:

  • A project completed successfully

  • A customer milestone

  • A particularly helpful contribution

  • A process improvement

  • A team or individual achievement

Recognition can help create a stronger sense of connection and reinforce positive behaviors.

8. Action Items

Finish the meeting by identifying what happens next.

Every important action item should have:

  • A clearly defined task

  • One owner

  • A deadline

  • Any relevant context

For example:

Action ItemOwnerDeadline
Finalize campaign briefAlexFriday
Send revised proposalMariaTuesday
Schedule customer reviewJamesWednesday

Avoid assigning an action item to an entire group whenever possible. When everyone owns a task, accountability can quickly become unclear.

How to Build a Better Staff Meeting Agenda

Creating the agenda is only the first step. How you use it can have an even bigger impact.

Share the Agenda Before the Meeting

Give participants enough time to review the agenda and prepare.

For recurring meetings, you can keep a shared document where team members add topics throughout the week. This prevents important issues from being forgotten at the last minute.

Assign Time to Each Topic

A simple time allocation can keep discussions under control.

For example:

  • 5 minutes — Opening and priorities

  • 10 minutes — Team updates

  • 15 minutes — Project progress

  • 15 minutes — Blockers and decisions

  • 5 minutes — Recognition

  • 5 minutes — Action items

If a discussion requires more time than expected, decide whether it should continue or be moved into a separate meeting.

Keep Discussions Focused

The facilitator should monitor the conversation and bring the group back to the agenda when necessary.

If an interesting but unrelated topic comes up, add it to a "parking lot" section and decide later whether it requires a separate discussion.

Make the Agenda Collaborative

A staff meeting shouldn't be designed entirely by one person.

Encourage team members to add questions, concerns, and discussion topics before the meeting. This creates a more inclusive process and helps ensure that the meeting addresses the issues that actually matter to the team.

Document Decisions During the Meeting

Don't rely on memory.

When the team makes an important decision, record it immediately along with any relevant context. This creates a reliable reference and reduces the chance of revisiting the same discussion later.

Review Action Items Before Ending

Reserve the final few minutes for a quick recap.

Confirm:

  1. What was decided?

  2. What needs to happen next?

  3. Who owns each task?

  4. When is each task due?

This simple habit can dramatically improve follow-through.

A Simple Staff Meeting Agenda Template

You can use the following template for a weekly staff meeting:

Staff Meeting — [Date]

Meeting Purpose:
[What should we accomplish during this meeting?]

Attendees:
[Names]

1. Quick Check-In — 5 minutes

  • Important personal or team updates

  • Quick wins

2. Key Updates — 10 minutes

  • Company news

  • Changes in priorities

  • Important announcements

3. Goals & Project Progress — 15 minutes

  • Progress since the last meeting

  • Upcoming milestones

  • Important metrics

4. Blockers & Challenges — 15 minutes

  • What is slowing the team down?

  • What support is needed?

  • What problems require a decision?

5. Discussion & Decisions — 10 minutes

  • Topics requiring group input

  • Decisions to be made

6. Recognition — 5 minutes

  • Team wins

  • Individual contributions

  • Milestones

7. Action Items — 5 minutes

  • Task

  • Owner

  • Deadline

How Often Should You Hold Staff Meetings?

The right frequency depends on your team's size, workload, and pace.

A weekly staff meeting works well for many teams because it creates a predictable opportunity to discuss priorities and remove blockers.

However, not every team needs a weekly meeting.

Consider a biweekly schedule if:

  • Priorities change slowly

  • Most collaboration happens asynchronously

  • There are few recurring blockers

On the other hand, teams working on fast-moving projects may benefit from more frequent, shorter check-ins.

The key is to choose a cadence that provides value rather than scheduling meetings simply because they are recurring.

Common Staff Meeting Mistakes to Avoid

Even a well-designed agenda can fail if the meeting itself isn't managed effectively.

Avoid these common mistakes:

Inviting too many people

Large meetings can make discussion slower and reduce participation. Invite the people who genuinely need to contribute.

Turning the meeting into a status-report session

If everyone spends several minutes reading out information that could have been shared asynchronously, very little time remains for meaningful discussion.

Having no clear outcome

Every agenda item should have a reason for being there. If a topic doesn't require discussion, a meeting may not be the right format.

Spending too much time on one topic

A single issue shouldn't automatically consume the entire meeting. Use time limits and move complex discussions into dedicated follow-ups when appropriate.

Ending without clear next steps

A productive conversation isn't enough. The team should leave knowing what happens next and who is responsible.

Use Your Staff Meetings to Drive Action

A great staff meeting agenda is more than a list of topics.

It creates a connection between priorities, conversations, decisions, and execution.

When employees know why they're meeting, have time to prepare, understand what needs to be decided, and leave with clearly assigned next steps, meetings become significantly more valuable.

The best approach is simple: keep the agenda focused, make it collaborative, protect people's time, and always finish with clear accountability.

Start with the template above, adapt it to your team's needs, and regularly ask whether each part of the meeting is still delivering value. Over time, your staff meetings can become less about sharing information and more about solving problems, making decisions, and moving important work forward.

Improving User Experience in Dynamics 365 Automatically Focusing and Highlighting Missing Required Fields

When developing custom validations in Microsoft Dynamics 365, one common challenge is guiding users directly to the field that requires attention. Displaying an error message is helpful, but forcing users to manually search through multiple tabs and sections can lead to frustration and reduced productivity.

In a recent project, I implemented a more user-friendly approach that automatically:

  • Identifies the missing required field

  • Expands the tab containing the field

  • Scrolls the page to the correct location

  • Sets focus on the field

  • Displays a visual notification explaining the issue

The Problem

A standard validation message such as:

"The following required fields have not been populated."

does not always help users quickly identify where the missing data is located, especially in large forms containing multiple tabs and sections.

Even calling:

var ctrl = Xrm.Page.getControl("dps_externalcashcoveragecode");

if (ctrl) {

ctrl.setFocus();

}

is often not enough. While the control receives logical focus, Dynamics 365 may not automatically scroll the page to make the field visible.

A Better Solution

The following approach combines notifications, tab expansion, focus management, and scrolling.

var ctrl = Xrm.Page.getControl("dps_externalcashcoveragecode");

if (ctrl) {

// Display field notification

ctrl.setNotification(

"This field is required before continuing.",

"REQ_FIELD"

);

// Expand the containing tab

var section = ctrl.getParent();

var tab = section.getParent();

tab.setVisible(true);

tab.setDisplayState("expanded");

setTimeout(function () {

// Set focus

ctrl.setFocus();

// Scroll to the field

var element = document.querySelector(

"[data-id='dps_externalcashcoveragecode.fieldControl']"

);

if (element) {

element.scrollIntoView({

behavior: "smooth",

block: "center"

});

}

}, 200);

}

Why This Works

This solution improves usability in several ways:

1. Clear Visual Feedback

The notification immediately tells users what action is required.

ctrl.setNotification(

"This field is required before continuing.",

"REQ_FIELD"

);

2. Automatic Tab Expansion

Users no longer need to manually navigate through tabs to find the missing field.

tab.setDisplayState("expanded");

`

3. Focus Management

The field becomes the active control, allowing users to start editing immediately.

ctrl.setFocus();

4. Automatic Scrolling

Using scrollIntoView() ensures the field is visible, even when located far down the form.

element.scrollIntoView({

behavior: "smooth",

block: "center"

});

Removing the Notification

After the user enters a valid value, the notification can be removed programmatically.

ctrl.clearNotification("REQ_FIELD");

Best Practice

If multiple fields are missing, avoid jumping from field to field. Instead:

  1. Validate all required fields.

  2. Display notifications on all invalid fields.

  3. Scroll and focus only on the first invalid field.

This provides a smoother and more predictable user experience while keeping the user informed about all validation issues.

Small usability improvements can have a significant impact on user adoption and productivity. Combining field notifications, automatic tab expansion, focus management, and scrolling creates a much more intuitive experience than simply displaying a generic error message.

For Dynamics 365 developers, investing a few extra lines of JavaScript can greatly improve form validation workflows and reduce user frustration when working with complex forms.

Stub Testing a Complete Guide to Reliable and Maintainable Software Tests

 Software testing is one of the foundations of reliable application development. As applications become increasingly complex, however, testing every component against real external dependencies can make test suites slow, fragile, and difficult to maintain.

This is where stub testing becomes extremely useful.

A stub allows developers to replace a real dependency with a controlled substitute that returns predefined responses. Instead of contacting a real database, external API, file system, or third-party service, a test can work with predictable data and focus on the behavior of the component being tested.

In this guide, we will explore what stub testing is, how it differs from mocks and fakes, how to design effective stubs, and how to use them in popular programming environments such as Python, JavaScript, and Java.

What Is Stub Testing?

A stub is a test double that provides predefined responses when it is called by the system under test.

For example, imagine an application that retrieves a user's profile from an external API. During a unit test, contacting the real API would introduce an unnecessary external dependency.

Instead, we can replace the API client with a stub that always returns a known user profile.

The test can therefore verify the application's logic without making a real network request.

This approach provides several important advantages:

  • Speed: tests avoid slow external services.

  • Reliability: external outages do not cause test failures.

  • Determinism: the same input produces predictable results.

  • Isolation: developers can test one component independently.

  • Flexibility: unusual or difficult scenarios can be simulated easily.

The main principle is simple: replace an external dependency with a controlled response so that the component under test can be evaluated in isolation.

Stub vs. Mock vs. Fake

Stub, mock, and fake are all examples of test doubles, but they serve different purposes.

Stub

A stub primarily provides predefined data or behavior.

For example:

When getUser(1) is called,
return { id: 1, name: "Alice" }

The goal is to provide the data required by the test.

Mock

A mock is generally used when the test also needs to verify interactions.

For example, a mock can verify that:

  • a method was called;

  • it was called with specific parameters;

  • it was called a particular number of times;

  • calls occurred in a specific order.

Mocks are therefore particularly useful when the interaction between components is itself part of the behavior being tested.

Fake

A fake is a simplified working implementation of a real component.

A common example is an in-memory database used instead of a production database. Unlike a simple stub, a fake may contain actual logic and state.

In practice, the choice depends on the objective of the test:

Test doubleMain purpose
StubProvide predefined responses
MockVerify interactions
FakeProvide a simplified working implementation

Keeping these concepts separate can make a test suite easier to understand and maintain.

Why Use Stub Testing?

One of the biggest advantages of stub testing is isolation.

Consider an application that calculates information using data retrieved from an external service. If the test depends directly on that service, a failure could have many possible causes:

  • the external service is unavailable;

  • the network connection fails;

  • the service returns unexpected data;

  • authentication expires;

  • the API changes;

  • the response takes too long.

None of these problems necessarily indicate a bug in the application code being tested.

A stub removes those variables.

The test can provide exactly the response required and concentrate on the application's behavior.

This makes failures easier to understand and significantly improves the reliability of automated testing.

How to Design an Effective Stub Test

Creating a good stub is not simply a matter of replacing every dependency with fake data. The stub should support the objective of the test without making the test unnecessarily complicated.

Define the Test Objective

Before creating a stub, determine exactly what you want to test.

Identify:

  • the component under test;

  • its inputs;

  • its expected outputs;

  • the dependency that needs to be replaced;

  • the scenarios that must be simulated.

A well-defined objective prevents the stub from becoming more complicated than the code it is testing.

Identify the Right Dependencies

Not every dependency needs to be stubbed.

Focus on components that are:

  • external;

  • slow;

  • unreliable;

  • expensive;

  • difficult to reproduce;

  • dependent on network resources;

  • dependent on unpredictable data.

Typical examples include APIs, databases, message queues, file systems, and third-party services.

Make Responses Deterministic

A good stub should behave predictably.

For example, if a test needs to verify successful authentication, the stub should consistently return a successful response.

You should also consider negative and edge cases, such as:

  • HTTP errors;

  • missing data;

  • invalid input;

  • timeouts;

  • empty responses;

  • unexpected values.

This makes it possible to test scenarios that may be difficult to reproduce with a real service.

Keep Stub Configuration Simple

If the same stub is used by multiple tests, consider centralizing its configuration.

A factory, helper function, fixture, or dedicated test utility can prevent duplicated configuration and make future changes easier.

Keep Stubs Updated

External interfaces evolve.

If an API changes its response format, the corresponding stubs may also need to be updated.

Outdated stubs can create a dangerous situation where tests continue to pass even though they no longer accurately represent real application behavior.

For this reason, stub maintenance should be treated as part of normal software maintenance.

Stub Testing in Python

Python provides several options for working with test doubles. The standard library includes unittest.mock, which can be used to replace dependencies during testing.

For example, imagine a function that retrieves a user profile:

def fetch_user_profile(user_id, api_client):
    response = api_client.get(
        f"https://api.example.com/users/{user_id}"
    )

    if response.status_code == 200:
        return response.json()

    raise ValueError("Unable to retrieve user profile")

A test can provide a controlled API client instead of making a real HTTP request:

def test_fetch_user_profile():
    class ApiStub:
        def get(self, url):
            return type(
                "Response",
                (),
                {
                    "status_code": 200,
                    "json": lambda self: {
                        "id": 1,
                        "name": "Alice"
                    }
                }
            )()

    result = fetch_user_profile(1, ApiStub())

    assert result == {
        "id": 1,
        "name": "Alice"
    }

The important idea is that the function does not know whether it is communicating with the real API or the test implementation.

The test therefore remains fast and deterministic.

Stub Testing in JavaScript

JavaScript and TypeScript developers frequently use testing frameworks such as Jest.

Consider a function that calculates a value using a rate retrieved from an external service:

export async function computeTax(income, api) {
    const rate = await api.getTaxRate();
    return income * rate;
}

A simple stub can provide a predictable tax rate:

test("computeTax uses the rate supplied by the stub", async () => {
    const apiStub = {
        getTaxRate: async () => 0.20
    };

    const tax = await computeTax(50000, apiStub);

    expect(tax).toBe(10000);
});

The test does not need to access a real external service.

This makes the test faster and ensures that the calculation is evaluated using known data.

Stub Testing in Java

Java developers can implement stubs manually or use libraries such as Mockito.

For example:

TaxApi apiStub = mock(TaxApi.class);

when(apiStub.getTaxRate()).thenReturn(0.15);

TaxService service = new TaxService(apiStub);

double result = service.calculateTax(40000);

assertEquals(6000, result, 0.001);

Here, getTaxRate() always returns the predefined value.

Mockito can also be used to verify interactions when the test needs to confirm that a particular method was called.

This illustrates an important distinction: a test may use a stub to provide data and mock-style verification to check interactions.

Common Mistakes When Using Stubs

Stub testing is powerful, but excessive or inappropriate use can reduce the quality of a test suite.

Overusing Stubs

If every dependency is replaced with a stub, tests can become disconnected from reality.

A test may pass even though the real components do not work correctly together.

Using Unrealistic Data

Stub responses should resemble realistic production scenarios.

If real APIs can return missing fields, errors, delays, or unexpected values, those scenarios should be represented in appropriate tests.

Forgetting Integration Tests

Unit tests with stubs are excellent for isolation, but they cannot completely replace integration testing.

Real integrations should still be tested in controlled environments to verify that components communicate correctly.

Creating Overly Complex Stubs

A stub should normally be simpler than the component it replaces.

If a stub contains complicated business logic, it may become another source of bugs and make tests harder to understand.

Best Practices for Maintainable Stub Tests

A few principles can make stub-based testing considerably more effective.

Use Clear Names

Names such as:

apiClientStub
databaseStub
paymentServiceStub
userRepositoryStub

make the purpose of each test double immediately understandable.

Test Both Success and Failure

Do not only simulate successful responses.

Include scenarios such as:

  • invalid requests;

  • missing resources;

  • server errors;

  • empty results;

  • timeouts;

  • malformed data.

Keep Tests Focused

Each test should have a clear purpose.

Avoid creating enormous test scenarios involving multiple unrelated stubs and complex configurations.

Review Stubs Regularly

As the production code and external services evolve, review the corresponding stubs.

Remove obsolete test doubles and update responses when interfaces change.

Combine Unit and Integration Testing

The strongest testing strategy usually combines different levels of testing.

Unit tests can use stubs to provide fast feedback, while integration tests can verify that real components work together correctly.

When Should You Use Stub Testing?

Stub testing is particularly useful when a component depends on something outside its direct control.

Typical examples include:

  • REST APIs;

  • payment services;

  • databases;

  • cloud services;

  • authentication providers;

  • file systems;

  • message queues;

  • external APIs;

  • third-party libraries.

A stub is especially valuable when the external dependency is slow, expensive, unreliable, or difficult to reproduce.

However, the objective should not be to eliminate every real dependency from testing.

Instead, use stubs where they provide meaningful isolation and combine them with integration and end-to-end tests where real system behavior needs to be validated.

Stub testing is a simple but powerful technique for building reliable automated tests.

By replacing complex or unpredictable dependencies with controlled responses, developers can create tests that are faster, more deterministic, and easier to troubleshoot.

The key is to use stubs strategically.

A good stub should be simple, predictable, realistic enough for the scenario being tested, and easy to maintain. At the same time, stub-based unit tests should complement—not replace—integration and end-to-end testing.

Whether you work with Python, JavaScript, Java, or another programming language, understanding how to use stubs effectively can significantly improve the quality and maintainability of your testing strategy.

Isolate what you need to test, control what you need to simulate, and keep your test doubles as simple as possible.

The Power of Active Listening Why Great Teams Listen Before They Act


Whether you are an athlete, coach, captain, or team manager, effective communication starts with one simple skill: listening.

Listening to teammates, coaches, opponents, and staff helps us understand different perspectives, identify needs, build trust, and create stronger relationships.

But there is a big difference between simply hearing someone and truly listening to them.

That is where active listening comes in.

What Is Active Listening?

Active listening means being fully engaged in a conversation.

It is not just about hearing the words someone says. It is about understanding their message, emotions, intentions, and point of view.

In sport, communication is often about much more than words. A player's body language, tone of voice, facial expressions, and attitude can communicate just as much as what they actually say.

An actively engaged listener gives the speaker their full attention. That means putting distractions aside, making eye contact, and being present in the moment.

Most importantly, active listening means trying to understand someone else's perspective without immediately judging it or imposing your own opinion.

You do not have to agree with your teammate or coach. But before responding, you should make sure you understand where they are coming from.

Why Does Active Listening Matter in Sport?

Active listening can have a powerful impact on both individual performance and team culture.

It Builds Trust and Stronger Relationships

Trust is one of the foundations of successful teams.

When athletes feel that their teammates and coaches genuinely listen to them, they are more likely to feel respected and valued. This creates an environment where people feel comfortable expressing concerns, sharing ideas, and asking for help.

For coaches, listening can also provide valuable insight into what athletes are experiencing, both on and off the field.

It Improves Understanding

A disagreement between teammates is not always about the issue itself. Sometimes, the real problem is that people have different experiences or expectations.

Active listening helps uncover the context behind someone's opinion.

Instead of simply thinking, "I disagree with you," an athlete can ask, "Why do you see it that way?"

That small change can lead to a much deeper understanding and prevent unnecessary conflict.

It Can Improve Team Performance

Sport is fast-paced. Players need to understand instructions, adapt to changing situations, and respond quickly to their teammates.

Good listening helps make communication more efficient.

When players listen carefully during training, meetings, tactical discussions, and feedback sessions, they are more likely to understand what is expected of them and execute their role correctly.

In other words, better listening can mean fewer misunderstandings and fewer mistakes.

How to Become a Better Listener

Active listening is a skill that can be developed with practice. Here are some simple techniques athletes and coaches can use.

Make Eye Contact

When someone is speaking to you, look at them and show that you are engaged.

You do not need to stare constantly, but appropriate eye contact and an open posture can communicate that you are paying attention.

Show Encouragement

Small reactions can reassure the other person that you are following the conversation.

A nod, a smile, or a simple "yes," "I understand," or "that makes sense" can encourage someone to continue speaking.

These small signals can be particularly important when discussing difficult topics.

Don't Interrupt

Give people the opportunity to finish their thoughts.

In a competitive sporting environment, it can be tempting to respond immediately, defend yourself, or explain your position. But interrupting can prevent you from hearing the complete message.

Let the person finish. Then respond.

Ask Open-Ended Questions

If something is unclear, ask for clarification.

Instead of making assumptions, use open-ended questions that encourage the other person to explain their perspective.

For example:

"Can you tell me more about what happened?"

"What did you feel was missing from the team's approach?"

The goal is not to prove someone wrong. The goal is to understand.

Paraphrase What You Heard

One of the most effective active-listening techniques is to repeat the message in your own words.

You might say:

"Just to make sure I understand, you're saying that you felt we didn't communicate well during the second half. Is that right?"

This gives the other person the opportunity to confirm what you understood or correct you if necessary.

It can prevent misunderstandings before they become bigger problems.

Active Listening Starts With Empathy

Perhaps the most important part of active listening is empathy.

In sport, everyone sees situations through their own experience.

A coach may focus on tactics and performance.

An athlete may be thinking about confidence, fatigue, motivation, or pressure.

A captain may be concerned about team chemistry.

Active listening allows us to temporarily step outside our own perspective and consider what someone else might be experiencing.

That does not mean abandoning your own opinion. It means making space for another person's point of view before deciding how to respond.

From Communication to Teamwork

Great teams are not built only through talent and physical preparation. They are built through trust, respect, understanding, and communication.

And communication begins with listening.

When athletes listen to one another, coaches listen to their players, and leaders listen to their teams, misunderstandings become easier to resolve and relationships become stronger.

The next time you are in a team meeting, training session, locker room, or difficult conversation, try something simple: listen before you respond.

You may discover that the most important part of communication is not having the right answer, but making sure you truly understand the person speaking.

The Art of Persuasion How to Influence, Inspire and Lead a Team



Behind every great performance, there is communication, leadership, trust, and the ability to bring people together around a common goal.

Coaches need to convince athletes to embrace a tactical approach. Captains need to motivate teammates. Players need to communicate ideas and influence decisions on and off the field.

This is where persuasion becomes an essential skill.

Persuasion is not about forcing people to agree with you. It is about presenting your ideas in a way that helps others understand your perspective, recognize its value, and ultimately move toward a shared objective.

Why Does Persuasion Matter in Sport?

A sports team is made up of different personalities, experiences, motivations, and opinions.

A coach may have a clear vision for how the team should play, but simply giving instructions does not guarantee that players will fully understand or believe in them.

The same applies to team captains. A captain does not always have formal authority over teammates. Leadership often comes from influence rather than position.

To create alignment, you need to communicate your ideas and persuade others to believe in the direction you are proposing.

Whether the goal is changing a tactical approach, improving team culture, or preparing for an important competition, persuasion can help turn individual opinions into collective action.

The Three Elements of Persuasion

More than 2,000 years ago, Greek philosopher Aristotle identified three fundamental modes of persuasion:

  • Ethos — credibility

  • Pathos — emotion

  • Logos — logic

These principles remain highly relevant in modern sport.

Ethos: Build Credibility

Ethos is about credibility and trust.

In sport, people are more likely to listen to someone they respect and trust. A coach earns credibility through experience, knowledge, consistency, and the ability to make sound decisions.

Athletes can also develop credibility through their performance, character, professionalism, and commitment to the team.

However, credibility is not built overnight.

It comes from consistently:

  • Doing what you say you will do

  • Supporting your teammates

  • Demonstrating professionalism

  • Making decisions based on the team's best interests

Pathos: Connect Through Emotion

Sport is emotional.

Confidence, fear, excitement, disappointment, pride, and motivation can all influence performance.

Pathos is the emotional side of persuasion. It is about understanding how you want your audience to feel and communicating in a way that creates that emotional connection.

Think about a coach speaking to a team before a championship game.

Statistics and tactics matter, but sometimes the message that players remember is the one that makes them believe in themselves and in each other.

A strong story, personal experience, or even a well-timed moment of humor can make a message more memorable and help people connect with it.

Logos: Use Logic and Evidence

Logos is the logical side of persuasion.

In sport, this often means using:

  • Statistics

  • Performance data

  • Tactical analysis

  • Evidence

  • Previous game results

For example, instead of simply telling a team that a particular strategy will work, a coach can demonstrate how the strategy has performed in previous games or how the team's data supports the decision.

Data does not need to overwhelm the audience. A few relevant statistics can often be more powerful than a presentation filled with numbers.

The Best Persuasive Messages Combine All Three

Ethos, pathos, and logos should not be treated as separate tools.

The most effective communication combines credibility, emotion, and logic.

A coach with strong credibility can make statistical information more convincing. A powerful statistic can create an emotional reaction. A personal story can strengthen the connection between a leader and the team.

Together, these elements create a much stronger message.

Know Your Audience

Before trying to persuade someone, understand who you are speaking to.

A message that motivates one athlete may have little impact on another.

Some players respond to statistics and technical explanations. Others are motivated by personal challenges, team goals, or emotional encouragement.

Understanding what your audience values allows you to adapt your communication without changing the fundamental message.

The key question is:

What matters to the people I am trying to influence?

Once you know the answer, you can communicate your idea in a way that feels relevant to them.

Choose a Clear Point of View

One common mistake in communication is trying to present every possible option without making a recommendation.

In sport, this can create confusion.

If a coach believes the team should change its defensive strategy, the players need to understand what the recommended approach is and why it is considered the best option.

That does not mean ignoring alternative perspectives.

A strong leader should understand different options, including their advantages and disadvantages, and be prepared to discuss them.

But ultimately, effective persuasion requires clarity and decisiveness.

Support Your Ideas With Data

Opinions are important, but evidence makes arguments stronger.

Performance data can help coaches and athletes move beyond personal preferences and focus on what is actually happening.

For example, statistics can reveal:

  • Where the team is losing possession

  • Which areas of the field create the most opportunities

  • How effectively players are pressing

  • Where defensive problems are occurring

  • Which tactical choices are producing better results

The goal is not to use data simply to prove that you are right.

The goal is to use evidence to help the team make better decisions.

Don't Ignore Objections

When someone disagrees with you, it can be tempting to dismiss their argument.

That is rarely effective.

Ignoring concerns can make you appear defensive and can damage trust within the team.

Instead:

  • Listen to objections

  • Discuss them openly

  • Ask questions

  • Explore different perspectives

  • Consider the advantages and disadvantages of each option

  • Give people the opportunity to explain why they disagree

This approach demonstrates that you are not simply trying to win an argument. You are trying to find the best solution for the team.

And sometimes, that solution will not be your original idea.

Great leaders understand that changing their mind when presented with better information is not weakness. It is good leadership.

Always Finish With a Call to Action

A persuasive conversation should lead somewhere.

Once everyone understands the argument and the group has reached a decision, make sure the next steps are clear.

In a sports environment, that might mean:

"Starting tomorrow, we'll use this defensive system in training. Everyone will have a specific role, and we'll review the results after the next two sessions."

Clear actions turn an idea into a plan.

Without a clear next step, even the most persuasive presentation can lose its impact.

Persuasion Is About Leadership, Not Winning

You will not win every discussion.

And that is perfectly fine.

The goal of persuasion is not to make sure everyone agrees with you every time. It is to create better conversations, encourage understanding, and help people move toward a common objective.

Over time, consistently communicating with credibility, empathy, logic, and clarity builds trust.

Your teammates learn that you listen.

Your athletes learn that your decisions have a purpose.

Your coaches and colleagues learn that you can present a strong argument while still respecting different perspectives.

That is when persuasion becomes more than a communication technique.

It Becomes a Leadership Skill

In sport, the strongest leaders are not necessarily the loudest people in the room.

They are the people who can inspire others to believe in a shared vision — and then turn that belief into action.