" MicromOne

Pagine

Understanding the AutoML Workflow: From Raw Data to Production

Automated Machine Learning (AutoML) is often described as a way to automate model selection and hyperparameter tuning. In practice, modern AutoML systems can automate a much broader machine learning workflow, from preparing datasets to evaluating models and generating production-ready pipelines.

For software developers, understanding this workflow is important because AutoML is not simply a "train button." It is an orchestration layer that combines data processing, feature engineering, model selection, hyperparameter optimization, validation, and deployment.

This article walks through the typical AutoML workflow and explains what happens at each stage.

1. Data Ingestion and Validation

Every AutoML workflow starts with data.

The system first loads the training dataset and determines its structure. Depending on the framework, this may involve detecting:

  • Numerical and categorical columns

  • Missing values

  • Text or timestamp fields

  • The target variable

  • Class distributions

  • Potentially irrelevant or duplicated features

Data validation is particularly important because machine learning algorithms generally expect structured and consistent input.

A typical pipeline might conceptually look like:

Raw Dataset
     |
     v
Schema Detection
     |
     v
Data Validation
     |
     v
Preprocessing

At this stage, an AutoML system may identify problems such as missing values, inconsistent data types, highly imbalanced classes, or columns that should not be used as predictors.

However, developers should not assume that automated validation can detect every data-quality problem. Business-specific constraints often require explicit validation rules.

2. Data Preprocessing

Once the dataset has been validated, AutoML applies preprocessing transformations.

Common operations include:

  • Missing-value imputation

  • Numerical feature scaling

  • Categorical encoding

  • Outlier handling

  • Feature normalization

  • Text vectorization

  • Date and time transformations

For example, a categorical feature such as:

country = ["US", "UK", "DE", "FR"]

may be transformed using one-hot encoding or another representation.

A key requirement is that preprocessing must be reproducible. The transformations applied during training must also be applied consistently during inference.

A robust AutoML pipeline therefore treats preprocessing as part of the model pipeline rather than as a separate manual operation.

3. Feature Engineering

Feature engineering is one of the most valuable—and potentially most complex—parts of AutoML.

Traditional machine learning workflows often rely heavily on domain experts to create useful features. AutoML systems attempt to automate some of this process.

For example, a timestamp could generate features such as:

year
month
day_of_week
hour
is_weekend

A transaction dataset could potentially generate aggregate features such as:

average_transaction_value
transactions_last_30_days
customer_lifetime_value

Some AutoML systems also perform feature selection, removing features that provide little predictive value.

Feature engineering can significantly improve model performance, but automated transformations must be designed carefully to avoid data leakage.

4. Train/Test Splitting and Cross-Validation

The next step is determining how models will be evaluated.

For a typical supervised learning problem, the dataset is divided into training and validation data, with a separate test set reserved for final evaluation.

A simplified structure is:

Dataset
   |
   +---- Training Set
   |
   +---- Validation Set
   |
   +---- Test Set

AutoML systems may use cross-validation instead of a single validation split.

For example, with five-fold cross-validation, the training data is divided into five subsets. The model is trained multiple times, using different subsets for validation.

This provides a more reliable estimate of generalization performance, particularly when the dataset is relatively small.

Developers should pay close attention to the splitting strategy. Random splitting is not appropriate for every problem. Time-series data, for example, generally requires time-aware validation to prevent future information from leaking into the training process.

5. Model Selection

Once the data pipeline is established, the AutoML system can evaluate different machine learning algorithms.

Depending on the task, candidates might include:

Classification

  • Logistic Regression

  • Decision Trees

  • Random Forests

  • Gradient Boosting

  • Support Vector Machines

  • Neural Networks

Regression

  • Linear Regression

  • Random Forest Regression

  • Gradient Boosting

  • Neural Networks

Other Tasks

Specialized AutoML systems may also support:

  • Time-series forecasting

  • Natural language processing

  • Computer vision

  • Recommendation systems

Rather than manually selecting one algorithm, AutoML evaluates multiple candidates under a common evaluation framework.

Conceptually:

                    +--> Model A --> Score
                    |
Training Pipeline --+--> Model B --> Score
                    |
                    +--> Model C --> Score

The goal is not necessarily to find the theoretically "best" algorithm. Instead, the system searches for a model configuration that performs well under the selected constraints and evaluation metric.

6. Hyperparameter Optimization

Selecting an algorithm is only part of the problem.

Most machine learning algorithms have hyperparameters that influence their behavior.

For example, a gradient-boosting model may have parameters controlling:

learning_rate
number_of_trees
maximum_depth
subsample_ratio

An AutoML system can automatically search through different combinations of these values.

Common optimization strategies include:

  • Grid search

  • Random search

  • Bayesian optimization

  • Evolutionary optimization

  • Hyperband and other resource-aware strategies

Modern AutoML systems often combine model selection with hyperparameter optimization rather than treating them as completely independent steps.

The search can therefore be represented as:

Algorithm
    +
Hyperparameters
    +
Preprocessing Choices
    |
    v
Candidate Pipeline
    |
    v
Evaluation

The system repeats this process until it reaches a specified time, compute, or experiment budget.

7. Evaluation and Optimization Metrics

AutoML needs an objective function to determine which candidate is better.

The metric depends on the business and technical requirements.

For classification, common metrics include:

  • Accuracy

  • Precision

  • Recall

  • F1 score

  • ROC-AUC

  • Log loss

For regression:

  • Mean Absolute Error (MAE)

  • Mean Squared Error (MSE)

  • Root Mean Squared Error (RMSE)

Choosing the correct metric is critical.

For example, consider a fraud-detection system where fraudulent transactions represent only a small percentage of all transactions. Optimizing for accuracy alone could produce a model that appears highly accurate while detecting very few fraudulent transactions.

The AutoML system can optimize only what it is instructed to optimize. Therefore, metric selection remains a developer and domain-expert responsibility.

8. Experiment Tracking

A serious AutoML workflow can generate hundreds or thousands of candidate experiments.

Each experiment may contain:

Dataset version
Preprocessing configuration
Algorithm
Hyperparameters
Validation strategy
Evaluation metrics
Training duration
Model artifacts

Experiment tracking becomes essential for reproducibility.

Instead of simply keeping the best model, production systems should maintain information about how that model was produced.

This allows developers to answer questions such as:

  • Which dataset was used?

  • Which features were selected?

  • Which hyperparameters produced the model?

  • Which validation strategy was used?

  • Why was this model selected?

  • Can the experiment be reproduced?

AutoML therefore fits naturally into modern MLOps architectures.

9. Selecting the Final Model

After the search process finishes, AutoML ranks the candidate pipelines according to the optimization objective.

However, the model with the highest validation score is not automatically the best production model.

Developers may also need to consider:

  • Inference latency

  • Memory consumption

  • Model size

  • Interpretability

  • Infrastructure costs

  • Fairness requirements

  • Robustness

  • Security

  • Operational complexity

For example, a slightly less accurate model may be preferable if it has significantly lower inference latency and is easier to deploy.

This introduces an important principle:

AutoML optimizes the objective you define, not necessarily the system you actually need.

10. Final Training

Once the best configuration has been identified, the final model can be trained using the appropriate training data.

Depending on the workflow, the final training stage may use more data than the individual experiments.

The resulting artifact should include not only the trained model but also the preprocessing pipeline required to transform production data into the expected model input.

Conceptually:

Raw Input
   |
   v
Preprocessing
   |
   v
Feature Transformation
   |
   v
Trained Model
   |
   v
Prediction

Keeping these components together reduces the risk of training-serving skew.

11. Deployment

After validation, the model can be deployed.

A common architecture exposes the model through an API:

Application
     |
     | HTTP/gRPC
     v
Inference Service
     |
     v
Preprocessing
     |
     v
Model
     |
     v
Prediction

Depending on the requirements, the model may run:

  • As a REST API

  • Inside a container

  • As a batch-processing job

  • On a cloud ML platform

  • At the edge

  • Directly inside an application

The deployment strategy should take latency, scalability, availability, and cost into account.

12. Monitoring in Production

Deployment is not the end of the AutoML workflow.

A model can perform well during training and gradually become less effective as production data changes.

Monitoring should therefore cover several dimensions.

System Metrics

Examples include:

  • CPU and memory usage

  • Request latency

  • Throughput

  • Error rates

  • Availability

Data Metrics

Examples include:

  • Missing-value rates

  • Feature distributions

  • Input ranges

  • Category frequencies

Model Metrics

When ground-truth labels become available, developers can monitor:

  • Accuracy

  • Precision

  • Recall

  • Error rates

  • Business-specific KPIs

This makes it possible to detect data drift and model degradation.

13. Retraining and Continuous Optimization

When the production environment changes, the model may need to be retrained.

A mature AutoML architecture can automate parts of this process:

Production Data
      |
      v
Monitoring
      |
      v
Drift Detection
      |
      v
Retraining Trigger
      |
      v
AutoML Search
      |
      v
Model Validation
      |
      v
Deployment

However, fully automated deployment should be approached carefully.

In many production environments, a better approach is to automatically train and evaluate new candidates while requiring an approval step before replacing the production model.

This creates a controlled continuous-learning workflow.

AutoML as a Software Engineering Problem

For software developers, the most useful way to think about AutoML is not as a replacement for machine learning expertise but as an automation framework.

The overall pipeline can be summarized as:

Data
 |
 v
Validation
 |
 v
Preprocessing
 |
 v
Feature Engineering
 |
 v
Model Search
 |
 v
Hyperparameter Optimization
 |
 v
Cross-Validation
 |
 v
Model Selection
 |
 v
Final Training
 |
 v
Deployment
 |
 v
Monitoring
 |
 +----> Retraining

Each stage introduces engineering decisions that cannot always be automated safely.

AutoML can reduce the amount of manual experimentation required, but developers still need to define the problem correctly, establish reliable data pipelines, select appropriate metrics, control computational resources, and integrate models into production systems.

AutoML automates a large portion of the machine learning experimentation lifecycle, transforming what could be a highly manual process into a repeatable pipeline.

For software developers, its real value lies in the ability to systematically explore different combinations of preprocessing techniques, algorithms, features, and hyperparameters while maintaining an experiment-driven workflow.

The most effective AutoML implementations therefore combine automation with engineering discipline.


Server Monitoring with htop

When you are developing and maintaining applications on Linux servers, understanding what is happening at the system level can save you a lot of time.

An application may suddenly become slow, a deployment may consume more resources than expected, or a background process may start using an unusual amount of CPU or memory. Before reaching for complex monitoring platforms, there is a simple tool that can provide an immediate overview of the system: htop.

htop is an interactive process viewer for Unix-like systems. It provides a real-time view of running processes and system resource usage, making it particularly useful when troubleshooting servers from the command line.

What Is htop?

htop is an interactive system-monitoring tool that can be used to inspect processes and understand how a Linux or Unix-like system is using its resources.

It is often considered a more user-friendly alternative to the traditional top command. Instead of displaying a mostly text-based list of processes, htop provides a more interactive interface with visual resource indicators and keyboard shortcuts.

A typical htop screen gives you information about:

  • CPU utilization

  • Memory usage

  • Swap usage

  • System load

  • Running processes

  • Process IDs (PIDs)

  • CPU and memory consumption per process

  • Process ownership

  • Process priority and scheduling information

For developers working directly on servers, this information can be extremely valuable.

Installing htop

On Debian- and Ubuntu-based systems, you can usually install htop with:

sudo apt install htop

On Fedora:

sudo dnf install htop

On Arch Linux:

sudo pacman -S htop

Once installed, start it with:

htop

Because htop is interactive, you can navigate through processes using the keyboard rather than repeatedly running commands.

Understanding the htop Interface

One of the main advantages of htop is that important information is visible immediately.

At the top of the interface, you will typically find resource meters for CPU, memory, and swap, followed by system information such as load average and uptime.

The process list occupies most of the screen.

A process may be displayed with information such as:

  • PID

  • User

  • Priority

  • Nice value

  • Virtual memory

  • Resident memory

  • CPU percentage

  • Memory percentage

  • Execution time

  • Command

This makes it possible to quickly identify processes that are consuming an unusual amount of resources.

Monitoring CPU Usage

One of the most common reasons to use htop is investigating high CPU usage.

Suppose an API server suddenly becomes slow. You connect to the machine and run:

htop

The CPU meters can immediately tell you whether the machine is under heavy CPU load.

You can then sort the process list by CPU consumption to identify the processes responsible for the load.

This is particularly useful when dealing with:

  • CPU-intensive application code

  • Background workers

  • Build processes

  • Compilers

  • Database workloads

  • Containers

  • Unexpected processes

A high CPU percentage does not automatically mean there is a problem. A process may legitimately need significant CPU resources. The important question is whether the usage is expected and whether it correlates with the performance problem you are investigating.

Investigating Memory Usage

Memory problems are another common source of server instability.

In htop, the memory meter provides an immediate overview of RAM usage, while the process list helps you identify which processes are consuming the most memory.

For example, you might discover that a particular application process is using several gigabytes of RAM.

That could lead to further investigation:

  1. Is the application expected to use that much memory?

  2. Has memory usage increased over time?

  3. Are multiple instances running?

  4. Is the process leaking memory?

  5. Is the server running out of available memory?

  6. Is swap being used heavily?

htop does not diagnose the root cause of a memory leak, but it is an excellent first step for identifying suspicious processes.

Using htop During Production Incidents

One of the biggest strengths of htop is its simplicity.

During an incident, you may not have time to configure a complete monitoring stack. If you can access the server through SSH, you can often launch htop within seconds.

For example:

ssh user@server
htop

You can then quickly determine whether the problem appears to be related to CPU, memory, or a specific process.

This can help answer an important first question:

Is the application slow because of the application itself, or because the server is under resource pressure?

That distinction can significantly change the debugging strategy.

Finding a Specific Process

On a busy server, the process list can contain hundreds of entries.

Instead of manually searching through them, htop provides interactive navigation and filtering capabilities.

You can use the search functionality to find processes by name or command.

For example, if you are investigating a Node.js application, you can search for processes related to Node:

node

Similarly, developers working with Python, Java, PHP, Go, or other runtimes can quickly narrow the process list to relevant applications.

This is much faster than manually inspecting the output of a large process listing.

Sorting Processes

Sorting is one of the most useful features when troubleshooting resource usage.

If CPU usage is the problem, sort processes by CPU consumption.

If memory usage is the problem, sort by memory consumption.

This immediately puts the most resource-intensive processes at the top of the list.

Instead of asking:

"What is consuming all the memory?"

you can often answer the question within seconds.

Managing Processes

htop is not only a monitoring tool. It also provides controls for interacting with processes.

Depending on your permissions, you can perform actions such as:

  • Sending signals to processes

  • Terminating processes

  • Changing process priority

  • Searching for processes

  • Filtering the process list

  • Viewing process details

For example, if a process has become completely unresponsive, you may be able to select it and send an appropriate signal.

However, process management should be used carefully on production systems. Killing the wrong process can cause service interruptions or data loss.

Monitoring should come before intervention.

Understanding Load Average

The load average displayed by htop is another useful indicator, but it is important to interpret it correctly.

Load average represents the number of tasks that are either running or waiting for system resources, depending on the operating system's accounting.

For example, a server with many CPU cores can naturally have a higher load average than a single-core system without necessarily being overloaded.

Therefore, avoid interpreting load average as a simple percentage.

Instead, compare it with the number of available CPU cores and other indicators such as CPU utilization, I/O wait, and application behavior.

htop and Containers

Modern applications frequently run inside containers.

Although htop operates at the host level, it can still be useful when investigating containerized workloads because container processes ultimately consume host resources.

If a Docker host is experiencing high CPU or memory usage, htop can help you identify which processes are responsible.

For container-specific investigation, however, you may also want to use tools such as:

docker stats

or the monitoring tools provided by your container orchestration platform.

The best approach is often to combine application-level, container-level, and host-level information.

htop vs. top

Linux already includes top on most systems, so why use htop?

The main difference is usability.

top is lightweight and widely available, making it an excellent tool for minimal environments and recovery situations.

htop, on the other hand, provides a more interactive experience. It makes it easier to navigate processes, sort information, search, and understand resource consumption visually.

For developers who regularly work on Linux servers, htop can therefore be a more convenient day-to-day troubleshooting tool.

That does not make top obsolete. Knowing both is useful, especially when working with minimal systems where htop may not be installed.

A Practical Troubleshooting Workflow

A simple workflow can make htop particularly effective.

When a server starts behaving unexpectedly:

1. Connect to the server

ssh user@server

2. Start htop

htop

3. Check CPU usage

Look for consistently high CPU utilization and identify the processes responsible.

4. Check memory

Look at RAM and swap usage and determine whether a specific process is consuming an unusual amount of memory.

5. Inspect suspicious processes

Check the process name, user, command, and resource consumption.

6. Correlate the information

Compare what you see with application logs, deployment activity, database metrics, and other monitoring systems.

7. Take action carefully

Only after identifying the likely cause should you consider restarting services, terminating processes, scaling resources, or making configuration changes.

This workflow turns htop into a fast first-response tool rather than simply a process viewer.

What htop Cannot Tell You

Although htop is extremely useful, it should not be treated as a complete observability platform.

It tells you a lot about what the operating system is doing, but not necessarily why your application is doing it.

For example, htop might show that your application is consuming 100% of a CPU core. It will not tell you which function or request is responsible.

For deeper investigations, you may need:

  • Application logs

  • Metrics

  • Distributed tracing

  • Database monitoring

  • Profiling tools

  • APM platforms

  • Container and orchestration metrics

The best production troubleshooting strategy combines these different levels of visibility.


Train a Neural Network

Training a neural network is not simply a matter of choosing a model, loading a dataset, and pressing the “train” button.

It is easy to write a few lines of Python that start the training process. It is much harder to understand why a model works, why it does not work, or whether it is achieving good results for the right reasons.

This is one of the most important concepts to understand when getting started with deep learning.

Frameworks such as PyTorch and TensorFlow have made it remarkably easy to build sophisticated neural networks. But this convenience can be misleading: a neural network can be perfectly valid from a programming perspective while being fundamentally wrong from a machine learning perspective.

The real challenge is therefore methodological.

Instead of continuously adding layers, changing optimizers, modifying the dataset, and experimenting with increasingly complex techniques, it is usually much more effective to proceed from simple to complex, validating every step before introducing the next one.

This approach makes debugging dramatically easier and helps you understand what is actually happening inside your model.

The First Mistake Thinking Deep Learning Is Plug-and-Play

When we start programming, we often rely on mature abstractions.

For example, we can use an HTTP library without understanding every detail of TCP/IP. The abstraction hides most of the complexity, and we can generally trust it to do what we expect.

Neural networks are different.

A seemingly simple operation such as:

loss = model(x)

hides a huge number of assumptions.

What exactly does x contain?

Are the labels correct?

Is the preprocessing consistent with the preprocessing used during training?

Is the loss function appropriate for the task?

Is the learning rate reasonable?

Is the model actually using useful information from the input?

Does the dataset contain duplicates?

Is there data leakage?

Are data augmentations being applied correctly?

A neural network can continue training even when one or more of these assumptions are wrong.

That is what makes deep learning debugging particularly challenging.

Neural Network Bugs Are Often Silent

In traditional software development, many errors immediately produce an exception.

If a function expects an integer and receives an incompatible object, the program may stop.

In machine learning, things are often less obvious.

You can have a program that:

  • runs without errors;

  • completes the entire training process;

  • produces apparently reasonable metrics;

  • and still generates a poor model.

This is one of the most dangerous aspects of machine learning.

Imagine building an image classifier.

During data augmentation, we decide to horizontally flip some images. However, because of a bug, the image transformation is not consistent with the corresponding annotation.

The program still runs.

The GPU keeps working.

The loss continues changing.

But we have introduced noise into the training data.

The same principle applies to many other situations:

  • learning rate that is too high or too low;

  • incorrect labels;

  • inappropriate normalization;

  • incorrect train/validation splits;

  • data leakage;

  • tensor dimension mistakes;

  • poor initialization;

  • excessive regularization;

  • incorrect data augmentation;

  • bugs in vectorized operations;

  • incorrectly calculated evaluation metrics.

For this reason, debugging is a fundamental machine learning skill.

First Rule: Understand Your Dataset

The first phase of a machine learning project should often be the phase in which you do not build the model yet.

Start by understanding the data.

Suppose you have 100,000 images for training an image classifier.

That does not necessarily mean you have 100,000 useful examples.

You may have:

  • duplicate images;

  • corrupted files;

  • highly imbalanced classes;

  • incorrect labels;

  • almost identical images appearing in both training and test sets;

  • unusually difficult examples;

  • distributions that differ significantly between training and production.

A larger neural network will not automatically solve these problems.

In fact, a more powerful model may simply learn the problems in your dataset more effectively.

What should you analyze?

At minimum, inspect the following.

Class distribution

If you have ten classes and one of them represents 70% of the dataset, accuracy alone can become misleading.

A trivial classifier that always predicts the dominant class could already achieve 70% accuracy while being completely useless for the other classes.

Depending on the problem, metrics such as:

  • precision;

  • recall;

  • F1 score;

  • balanced accuracy;

  • ROC-AUC;

  • confusion matrices

may provide a much better picture.

Data quality

Look for:

  • corrupted samples;

  • missing values;

  • inconsistent formats;

  • suspicious labels;

  • outliers;

  • duplicates.

For images, inspect actual samples rather than relying exclusively on numerical statistics.

For text, read examples manually.

For tabular data, examine distributions and representative rows.

Machine learning is ultimately about data, and you cannot properly debug data you have never looked at.

Start With the Simplest Possible Model

A common beginner mistake is to start with a sophisticated architecture.

You find a new paper.

You copy the architecture.

You add attention mechanisms, residual connections, normalization layers, sophisticated augmentation, multiple learning-rate schedules, and a large pretrained backbone.

Then the model does not work.

At that point, what exactly is wrong?

You have too many variables changing at the same time.

A much better strategy is to establish a simple baseline.

For example, if you are solving an image classification problem, start with a relatively small convolutional network.

If you are working with tabular data, start with a simple baseline such as logistic regression, a decision tree, or a small gradient-boosted model.

If you are working with text, start with a simple representation and a relatively straightforward classifier before introducing a large language model or a complex architecture.

The baseline is not supposed to win the competition.

Its purpose is to answer a much more important question:

Can the entire machine learning pipeline work at all?

Make the Model Overfit a Tiny Dataset

This is one of the most useful debugging techniques in deep learning.

Take a tiny subset of your training data.

For example:

small_batch = dataset[:32]

Then try to train your model exclusively on those examples.

The objective is simple:

the model should be able to memorize them.

If the model cannot drive the training loss very low on a tiny dataset, there is probably a fundamental problem somewhere in your pipeline.

Possible causes include:

  • incorrect labels;

  • broken preprocessing;

  • incorrect loss function;

  • model architecture problems;

  • optimizer configuration;

  • learning-rate problems;

  • tensors with incorrect dimensions;

  • parameters that are not being updated;

  • accidental detachment from the computation graph.

This experiment is extremely valuable because it removes much of the complexity of generalization.

You are no longer asking:

Can the model learn the underlying distribution?

You are asking:

Can the model learn these few examples?

If the answer is no, adding more data or making the network larger is unlikely to solve the fundamental problem.

Separate Memorization From Generalization

A neural network has two different jobs.

First, it needs to learn the training examples.

Second, it needs to generalize to examples it has never seen.

These are related but different problems.

Consider two scenarios.

Scenario A

Training accuracy: 20%

Validation accuracy: 19%

The model is not even learning the training set.

This suggests an optimization, implementation, data, or modeling problem.

Scenario B

Training accuracy: 99%

Validation accuracy: 65%

Now the model can memorize the training data, but it does not generalize well.

This is a fundamentally different problem.

Possible causes include:

  • overfitting;

  • insufficient training data;

  • distribution shift;

  • noisy labels;

  • excessive model capacity;

  • inappropriate regularization;

  • train/validation mismatch.

The distinction is crucial.

Do not try to solve a generalization problem before proving that the model can learn the training data.

Training and Validation Must Be Clearly Separated

A common source of misleading results is contamination between datasets.

The usual setup is:

Dataset
   │
   ├── Training set
   │
   ├── Validation set
   │
   └── Test set

The training set is used to update model parameters.

The validation set is used to make decisions about the model and its hyperparameters.

The test set should ideally remain untouched until the final evaluation.

Why?

Because if you repeatedly optimize your model against the test set, you are indirectly training on it.

This produces an overly optimistic estimate of real-world performance.

The test set should represent data that the model has never influenced during development.

Data Leakage Can Destroy Your Evaluation

Data leakage occurs when information that should be unavailable to the model becomes accessible during training.

Consider a medical prediction problem.

Suppose you want to predict whether a patient will develop a condition in the future.

If a feature in your dataset was recorded only after the condition appeared, the model may achieve excellent validation performance.

But that performance is meaningless.

The feature would not actually be available when the prediction needs to be made.

Leakage can happen in many less obvious ways:

  • duplicated samples across train and test;

  • preprocessing performed before the dataset split;

  • future information included in features;

  • patient-level data split incorrectly;

  • temporal information leaking across datasets.

A model with leakage can look brilliant during development and fail immediately in production.

Overfit First, Then Regularize

Regularization techniques are extremely useful.

But introducing them too early can make debugging harder.

Suppose your model uses:

  • dropout;

  • weight decay;

  • aggressive data augmentation;

  • label smoothing;

  • early stopping;

  • a very small network.

The model performs poorly.

What is the cause?

Maybe the architecture is wrong.

Maybe the data is wrong.

Or perhaps you simply made the learning problem unnecessarily difficult.

A useful development strategy is:

  1. Build a simple model.

  2. Make sure it can overfit a tiny dataset.

  3. Train it on the full training set.

  4. Measure the generalization gap.

  5. Only then introduce regularization if necessary.

Regularization should solve a demonstrated problem rather than being added simply because it is considered “best practice.”

Keep the Initial Configuration Simple

Another common mistake is changing many hyperparameters simultaneously.

Imagine changing all of these at once:

learning rate
batch size
optimizer
weight decay
dropout
architecture
augmentation
scheduler
activation function

The model improves.

But why?

You do not know.

This makes experimentation much less informative.

Instead, change one important variable at a time whenever practical.

For example:

Experiment 1 → learning rate = 1e-3
Experiment 2 → learning rate = 3e-4
Experiment 3 → learning rate = 1e-4

Now you have information about the effect of the learning rate.

This may seem slower.

In practice, it is often much faster because every experiment teaches you something.

The Learning Rate Is Often More Important Than You Think

The learning rate controls the size of the updates applied to the model parameters.

A simplified gradient descent update can be written as:

[
\theta_{t+1} = \theta_t - \eta \nabla_\theta L(\theta_t)
]

where:

  • (\theta) represents the model parameters;

  • (L) is the loss;

  • (\nabla_\theta L) is the gradient;

  • (\eta) is the learning rate.

If the learning rate is too large, optimization can become unstable.

The loss may oscillate or even explode.

If it is too small, training may progress extremely slowly.

A practical approach is therefore to treat the learning rate as one of the first parameters worth investigating.

Before modifying the architecture, ask:

Is the optimizer actually taking useful steps?

Monitor the Loss, Not Just Accuracy

Accuracy is useful, but it is only one signal.

During training, monitor at least:

  • training loss;

  • validation loss;

  • training accuracy;

  • validation accuracy.

A typical training curve can reveal problems immediately.

For example:

Epoch       Train Loss       Validation Loss

1           1.80             1.85
2           1.35             1.48
3           0.95             1.20
4           0.60             1.15
5           0.35             1.30
6           0.20             1.50

Here the training loss keeps decreasing while the validation loss begins increasing.

That is a classic sign of overfitting.

The important point is that curves contain much more information than a final number.

Instead of saying:

“The model achieved 84% accuracy.”

you should ask:

“How did the model reach 84% accuracy?”

The trajectory often tells you what to do next.

Build Instrumentation Into Your Training Code

A serious training script should make the experiment observable.

At minimum, record:

model configuration
dataset version
training parameters
learning rate
batch size
optimizer
training loss
validation loss
training metrics
validation metrics
random seed
checkpoint information

A simple experiment log can save hours of work.

Without it, you may eventually ask:

“Which version of the model produced this result?”

And discover that you no longer know.

Reproducibility is not an academic luxury.

It is a practical debugging tool.

Check Gradients and Parameters

When a model refuses to learn, inspect what is actually happening inside the network.

For example, check whether gradients exist and whether they have reasonable magnitudes.

A simplified debugging pattern might look like:

loss.backward()

for name, parameter in model.named_parameters():
    if parameter.grad is not None:
        print(
            name,
            parameter.grad.abs().mean().item()
        )

If gradients are always zero, extremely large, or unexpectedly missing, you have an important clue.

You should also verify that parameters are actually changing.

A neural network cannot learn if its parameters are never updated.

This sounds obvious, but in larger training pipelines it is surprisingly easy to introduce a bug that prevents part of the model from receiving useful gradients.

Do Not Trust the Model Just Because the Numbers Look Good

A model can obtain good metrics for the wrong reasons.

Imagine an image classifier that distinguishes between two categories.

You achieve 95% validation accuracy.

Excellent?

Maybe.

But suppose all positive examples were photographed indoors and all negative examples outdoors.

The model might simply learn the background.

It has not learned the concept you care about.

This is why inspecting predictions is so important.

Look at:

  • correct predictions;

  • incorrect predictions;

  • high-confidence mistakes;

  • low-confidence predictions;

  • examples near the decision boundary.

A confusion matrix can also reveal systematic failures between classes.

The goal is not simply to maximize a metric.

The goal is to understand what the model has actually learned.

Visualize Everything You Can

Machine learning is an experimental discipline.

Visualization is one of the fastest ways to discover problems.

Useful visualizations include:

Dataset samples

Look at representative examples from every class.

Training curves

Plot training and validation loss.

Accuracy curves

Compare training and validation performance.

Confusion matrix

Identify classes that the model systematically confuses.

Predictions

Inspect the model's most confident mistakes.

Feature distributions

Check whether training and validation data appear to come from comparable distributions.

A single visualization can sometimes reveal a problem that would otherwise require dozens of experiments to diagnose.

Use a Baseline Before Chasing State of the Art

It is tempting to immediately search for the latest architecture.

But sophisticated models are not always the right starting point.

A baseline gives you a reference point.

For example:

Baseline model       → 72%
Improved preprocessing → 78%
Better architecture    → 81%
Regularization         → 83%
Data improvement       → 87%

Now you understand where the gains are coming from.

This is much more useful than jumping directly to a complex model that achieves 87% without knowing why.

In real projects, understanding the source of improvements is often more valuable than achieving a slightly better score.

When the Model Does Not Learn, Go Backward

One of the most useful habits in machine learning is knowing when to simplify.

If the model does not learn:

Do not immediately add complexity.

Instead, move backward.

Check:

  1. Is the dataset correct?

  2. Are the labels correct?

  3. Does the preprocessing work?

  4. Can the model overfit a tiny batch?

  5. Are gradients flowing?

  6. Are parameters changing?

  7. Is the loss appropriate?

  8. Is the learning rate reasonable?

  9. Does the baseline work?

  10. Only then: does the architecture need improvement?

This creates a debugging hierarchy.

The lower levels should be validated before moving to the higher ones.

Complexity Should Be Earned

A complex neural network should exist for a reason.

Adding another component should ideally answer a specific question:

“What problem am I trying to solve with this change?”

For example:

Problem: training performance is high but validation performance is poor.

Possible response: investigate overfitting and introduce appropriate regularization.

Or:

Problem: the model cannot capture the required spatial structure.

Possible response: consider a more appropriate architecture.

Or:

Problem: the input distribution is highly variable.

Possible response: investigate preprocessing or data augmentation.

This is much better than randomly adding techniques because they appear frequently in successful architectures.

A Practical Workflow for Beginners

A robust workflow might look like this:

1. Understand the problem
        ↓
2. Inspect the dataset
        ↓
3. Verify labels and preprocessing
        ↓
4. Create train/validation/test splits
        ↓
5. Build a simple baseline
        ↓
6. Overfit a tiny dataset
        ↓
7. Train on the complete dataset
        ↓
8. Inspect training and validation curves
        ↓
9. Analyze errors
        ↓
10. Identify the actual bottleneck
        ↓
11. Change one important thing
        ↓
12. Measure the result
        ↓
13. Repeat

This process may look less exciting than immediately implementing the latest neural network architecture.

But it is far more reliable.

 

The Most Important Skill: Debugging

Learning machine learning is sometimes presented as a mathematical challenge.

Mathematics is certainly important.

You should understand concepts such as:

  • gradients;

  • optimization;

  • probability;

  • loss functions;

  • regularization;

  • generalization;

  • linear algebra.

But practical machine learning also requires another skill:

debugging.

You need to learn how to look at an unexpected result and formulate hypotheses.

For example:

“The training loss is not decreasing. The model probably cannot optimize this problem.”

Then test it.

Or:

“Training accuracy is high but validation accuracy is poor. I should investigate overfitting or distribution differences.”

Then test it.

Or:

“The model is extremely confident about incorrect examples. I should inspect those samples.”

Then test it.

Machine learning becomes much easier when every experiment is treated as a hypothesis test.

Think Like a Scientist, Not Like a Gambler

A bad experimentation cycle looks like this:

Change everything
      ↓
Run training
      ↓
Look at accuracy
      ↓
Change everything again

A better cycle is:

Observe
   ↓
Form a hypothesis
   ↓
Change one relevant variable
   ↓
Run an experiment
   ↓
Measure
   ↓
Interpret
   ↓
Repeat

This distinction is fundamental.

The objective of an experiment is not merely to improve the metric.

The objective is to learn something about the system.

Sometimes an experiment that makes the model worse is actually extremely useful because it tells you what does not matter.

A Checklist Before Blaming the Architecture

When a model performs badly, use this checklist before replacing the architecture:

Data

  • Are the labels correct?

  • Are there duplicates?

  • Are there corrupted examples?

  • Are the classes balanced?

  • Is the training distribution representative?

Splits

  • Is there leakage?

  • Are train and validation samples genuinely independent?

  • Is the test set untouched?

Preprocessing

  • Are normalization and transformations correct?

  • Are training and inference preprocessing pipelines consistent?

  • Are augmentations preserving the meaning of the label?

Model

  • Can it overfit a tiny dataset?

  • Are all parameters trainable?

  • Are gradients flowing?

  • Is the loss function appropriate?

Optimization

  • Is the learning rate reasonable?

  • Is the optimizer configured correctly?

  • Is the batch size appropriate?

  • Is the loss actually decreasing?

Evaluation

  • Are the metrics appropriate?

  • Have you inspected individual predictions?

  • Have you examined the worst errors?

  • Are validation results representative of real-world performance?

Only after these questions have reasonable answers should you start blaming the architecture.

The hardest part of training neural networks is often not writing the model.

It is developing a reliable process for discovering what is wrong.

A successful machine learning workflow is usually built on a few fundamental principles:

  • Understand the data before optimizing the model.

  • Start with a simple baseline.

  • Prove that the model can memorize a tiny dataset.

  • Separate training problems from generalization problems.

  • Keep training, validation, and test data properly isolated.

  • Monitor losses and metrics throughout training.

  • Inspect predictions instead of trusting aggregate numbers.

  • Change one important variable at a time.

  • Keep experiments reproducible.

  • Add complexity only when you have identified a reason for it.

The goal is not to build the most sophisticated neural network possible.

The goal is to build a system that you understand.

Once you can reliably determine why a model is failing, improving it becomes much less mysterious.

And this is perhaps the most important transition when moving from simply using machine learning libraries to actually doing machine learning.

Preventing Invalid Operations with Form Save Validation in Dynamics 365

When developing customizations for Dynamics 365, it's common to trigger business logic from a command button or a custom JavaScript action. However, there's one important aspect that developers often overlook: ensuring that the current form contains valid and saved data before executing the logic.

In this article, I'll show a simple but effective approach to validate the form by forcing a save operation before continuing with the custom process.

The Problem

Imagine a custom button that creates a related record, such as an Article, based on the current Request.

If the user has modified the form but some required fields are missing or a business rule prevents the record from being saved, your custom logic may still execute, resulting in inconsistent or incomplete data.

Instead, it's a good practice to validate the form by attempting to save it first.

The Solution

The following code attempts to save the current form before executing the remaining business logic.

try {
    await formContext.data.save();
} catch (saveError) {
    Xrm.Navigation.openAlertDialog({
        title: "Warning!",
        text: "The request data is not valid. Before creating the article, verify the request information."
    });
    return;
}

How It Works

The implementation is straightforward:

  1. formContext.data.save() attempts to save the current record.

  2. If the save succeeds, the script continues with the remaining logic.

  3. If the save fails because of validation errors, required fields, Business Rules, or server-side validation, an exception is thrown.

  4. The catch block displays a friendly message to the user and stops the execution with a return.

This approach guarantees that the subsequent business logic only runs on valid and successfully saved data.

Why This Is Better

Compared to manually checking every required field, forcing a save provides several advantages:

  • It respects Dynamics 365 native validation.

  • It automatically handles Business Rules.

  • It validates server-side plugins and synchronous processes.

  • It reduces the amount of custom validation code.

  • It prevents downstream processes from working with invalid data.

Best Practices

When using this pattern, keep a few recommendations in mind:

  • Use await so the save operation completes before continuing.

  • Catch the exception to provide a clear and user-friendly error message.

  • Exit the function immediately after the failed save.

  • Let Dynamics 365 handle validation instead of duplicating business logic in JavaScript.

A simple try...catch around formContext.data.save() can significantly improve the reliability of your Dynamics 365 customizations.

Rather than assuming the current form is valid, let the platform perform its built-in validation first. This small change helps prevent inconsistent data, avoids unexpected errors later in the process, and provides a much better user experience.

Sometimes, the simplest solutions are also the most effective.


Building a React Web Resource for Microsoft Dynamics 365 CRM with React App Rewired

Modernizing the user experience in Microsoft Dynamics 365 CRM often means bringing modern frontend technologies into an established platform. While Power Apps Component Framework (PCF) is Microsoft's recommended approach for many scenarios, traditional Web Resources remain an excellent option for complex pages, dialogs, preview screens, and standalone applications.

In this article, we'll build a React-based Web Resource using Create React App and React App Rewired, customize the build output for Dynamics 365, and discuss when a Web Resource is a better choice than a PCF component.

Why Use React for Dynamics 365?

React offers several advantages when developing custom interfaces for Dynamics 365:

  • Component-based architecture

  • Excellent TypeScript support

  • Large ecosystem

  • Easy state management

  • Rich UI libraries such as Fluent UI

  • Easy integration with REST APIs and Dataverse Web API

Instead of writing plain HTML and JavaScript, React allows you to organize your application into reusable components that are easier to maintain.

Project Structure

A typical project uses Create React App together with React App Rewired.

Example dependencies:

  • React 19

  • TypeScript

  • Fluent UI React Components

  • React App Rewired

  • Xrm Type Definitions

The package configuration includes scripts like:

"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build"
}

Using React App Rewired allows customization of the webpack configuration without ejecting from Create React App.

Why Customize the Build?

Dynamics 365 Web Resources expect predictable file names.

The default Create React App build generates hashed filenames such as:

main.84af73.js
main.27aa5.css

These change with every build, making deployment inconvenient.

Instead, we configure webpack to produce fixed names like:

dps_contentpreview.js
dps_contentpreview.chunk.js
css/main.css

This makes importing Web Resources into Dynamics much easier.

Customizing the Output Folder

The first customization changes the build destination.

paths.appBuild = paths.appBuild.replace(
    "build",
    "../../dps_/pages/dps_contentpreview"
);

Instead of generating a local build folder, the compiled files are copied directly into the Dynamics solution folder.

This saves an extra copy step during development.

Customizing JavaScript Output

Webpack normally creates hashed bundles.

We override them:

config.output.filename = "dps_contentpreview.js";
config.output.chunkFilename = "dps_contentpreview.chunk.js";

Benefits include:

  • predictable deployment

  • easier solution packaging

  • no need to update Web Resource references after every build

Customizing CSS Output

The same principle applies to CSS.

config.plugins[5].options.filename = "css/[name].css";
config.plugins[5].options.moduleFilename = "css/[name].chunk.css";

Keeping CSS files stable simplifies deployment and version control.

Using Fluent UI

This project uses Fluent UI v9.

"@fluentui/react-components"

Fluent UI provides components that closely match Microsoft's design language, resulting in interfaces that feel native inside Dynamics 365.

Examples include:

  • Buttons

  • Dialogs

  • Tables

  • Cards

  • Tooltips

  • Inputs

  • Dropdowns

TypeScript Support

Using TypeScript greatly improves development.

Benefits include:

  • IntelliSense

  • Compile-time error checking

  • Better refactoring

  • Strong typing for Dynamics APIs

Adding:

"@types/xrm"

provides typing for the Xrm namespace, making interactions with the Dynamics client API much safer.

Accessing the Dynamics Context

Inside a Web Resource, the CRM context can be accessed using:

const formContext = parent.Xrm.Page;

or, in modern implementations:

const globalContext = parent.Xrm.Utility.getGlobalContext();

From there you can:

  • retrieve user information

  • access organization settings

  • execute Dataverse Web API requests

  • navigate to records

  • open dialogs

  • display notifications

Calling the Dataverse Web API

React works very well with the Dataverse Web API.

Typical operations include:

  • RetrieveMultiple

  • Retrieve

  • Create

  • Update

  • Delete

  • Custom Actions

  • Custom APIs

Using async/await makes the code much easier to read than older XMLHttpRequest implementations.

Web Resource vs PCF

One of the most common questions is:

Should I build a Web Resource or a PCF component?

The answer depends on the scenario.

Choose a Web Resource when

  • Building an entire application

  • Creating dashboards

  • Developing preview pages

  • Building administration tools

  • Displaying complex reports

  • Implementing wizard-like interfaces

  • Creating rich dialogs

A Web Resource gives you full control over the page.

Choose PCF when

  • Replacing a form field

  • Creating custom controls

  • Enhancing grids

  • Building reusable UI components

  • Integrating directly with form data

PCF components integrate deeply with the Power Platform lifecycle and are the recommended choice for reusable controls.

Advantages of Web Resources

  • Easier migration from existing JavaScript projects

  • Full React application

  • No PCF lifecycle complexity

  • Complete routing support

  • Freedom to use almost any React library

  • Easier debugging

Advantages of PCF

  • Native Power Platform integration

  • Better form lifecycle support

  • Automatic responsiveness

  • Strong metadata integration

  • Better ALM support

  • Standard deployment model

Deployment Considerations

When deploying React Web Resources, consider the following:

  • Keep filenames stable.

  • Minimize bundle size.

  • Use production builds.

  • Avoid unnecessary dependencies.

  • Separate large components using lazy loading.

  • Keep Fluent UI versions consistent across projects.

  • Test inside different Dynamics apps (Sales, Customer Service, Model-driven Apps).

Performance Tips

To improve performance:

  • Use React.memo where appropriate.

  • Lazy-load large modules.

  • Cache API responses.

  • Reduce unnecessary re-renders.

  • Bundle only the libraries you actually use.

  • Enable production optimizations.

Since Web Resources are loaded inside Dynamics, every kilobyte matters.

Is React Still a Good Choice for Dynamics?

Absolutely.

React remains one of the best technologies for building rich user interfaces inside Dynamics 365.

When combined with TypeScript, Fluent UI, and the Dataverse Web API, it provides an excellent developer experience while producing highly maintainable applications.

PCF is the preferred solution for custom controls embedded directly into forms and grids, but React Web Resources continue to be an excellent option for larger applications, dashboards, and standalone experiences where complete control over the interface is required.

Choosing between Web Resources and PCF should be driven by the type of solution you're building rather than by trends. In many enterprise projects, both approaches coexist successfully: PCF components enhance individual form elements, while React Web Resources deliver sophisticated pages and workflows that extend the capabilities of Dynamics 365.

Arriva QUERY The New HTTP Method for Complex Queries

The web has relied on the same set of HTTP methods for decades: GET, POST, PUT, PATCH, and DELETE. While these methods have proven reliable, modern APIs are increasingly dealing with highly complex search requests that don't fit neatly into the traditional model.

A new proposal, known as QUERY, aims to address this limitation by introducing a dedicated HTTP method specifically designed for complex read-only queries.

Why GET Isn't Always Enough

The GET method is ideal for retrieving resources using simple URL parameters. However, it has several drawbacks when queries become more sophisticated:

  • URLs can become extremely long.

  • Nested filters are difficult to represent.

  • Complex JSON structures cannot be included in the request body.

  • Long URLs may exceed browser or server limits.

For example, searching a large product catalog with dozens of filters quickly becomes impractical using query parameters alone.

Why POST Isn't the Perfect Solution

Many developers solve this problem by using POST requests for searches.

While this works technically, POST was originally intended for operations that create or modify server-side state. Using POST for read-only searches introduces several disadvantages:

  • Caching becomes less efficient.

  • API semantics become less clear.

  • HTTP tooling cannot easily distinguish between read and write operations.

  • Monitoring and optimization become harder.

Enter the QUERY Method

The proposed QUERY HTTP method provides a clean solution.

Like GET, QUERY is intended for safe, read-only operations. Unlike GET, it allows clients to send a request body containing structured data, typically JSON.

Example:

QUERY /products HTTP/1.1
Content-Type: application/json

{
  "category": "laptops",
  "price": {
    "min": 800,
    "max": 2000
  },
  "brands": [
    "Dell",
    "Lenovo",
    "Framework"
  ],
  "sort": "price"
}

This approach makes complex filtering much easier while preserving the semantics of a read-only request.

Main Benefits

The QUERY method offers several advantages:

  • Cleaner API design.

  • Better support for complex filtering.

  • JSON request bodies.

  • Easier integration with modern applications.

  • Potential compatibility with caching mechanisms designed for safe requests.

  • Clear separation between reading and modifying data.

Potential Use Cases

QUERY could become particularly useful for:

  • E-commerce search engines

  • Analytics dashboards

  • Business intelligence platforms

  • Geographic Information Systems (GIS)

  • AI-powered search APIs

  • Graph-like querying without adopting GraphQL

Current Status

It's important to note that QUERY is not yet part of the official HTTP standard and browser, server, and proxy support is still evolving. Developers should verify compatibility before using it in production environments.

Nevertheless, the proposal reflects an important trend: modern web applications increasingly require expressive, structured, and efficient query mechanisms that traditional GET requests cannot easily provide.

As APIs continue to grow in complexity, HTTP itself must evolve. The proposed QUERY method is an elegant attempt to bridge the gap between the simplicity of GET and the flexibility of POST while preserving proper HTTP semantics.

Whether QUERY becomes a widely adopted standard remains to be seen, but it represents an exciting step toward more expressive and developer-friendly web APIs.


How to Enable Text Selection and Right-Click Using JavaScript

Many websites disable text selection, the context menu, or keyboard shortcuts to prevent accidental copying or to customize the user experience. While these restrictions may serve a purpose, they can also interfere with legitimate activities such as taking notes, using translation tools, or accessing browser features.

This article demonstrates a simple JavaScript snippet that restores standard browser interactions by removing common client-side restrictions.


What This Script Does

The script performs several actions:

  • Re-enables text selection.

  • Restores the browser's right-click context menu.

  • Removes a common popup element if it exists.

  • Removes fixed-position overlays that may block page interaction.

  • Prevents page scripts from intercepting common mouse and clipboard events.

Because the script runs entirely in your browser, it only affects the current page during the current browsing session.


JavaScript Code

(() => {
    // Enable text selection
    const style = document.createElement("style");
    style.textContent = `
        * {
            user-select: text !important;
            -webkit-user-select: text !important;
            -moz-user-select: text !important;
            pointer-events: auto !important;
        }
    `;
    document.head.appendChild(style);

    // Remove popup
    document.querySelector("#notRemoverPopup")?.remove();

    // Remove fixed overlays
    document.querySelectorAll("[style*='z-index'], .modal, .overlay").forEach(el => {
        if (getComputedStyle(el).position === "fixed") {
            el.remove();
        }
    });

    // Restore context menu and clipboard events
    ["contextmenu","copy","cut","paste","selectstart","mousedown","mouseup","dragstart"].forEach(evt => {
        window.addEventListener(evt, e => e.stopImmediatePropagation(), true);
    });

    console.log("Restrictions removed.");
})();


How It Works

Restores Text Selection

The script injects CSS rules that override restrictions such as:

  • user-select: none

  • -webkit-user-select: none

This allows text to be highlighted again.

Removes a Popup

Some websites display a popup with the ID:

#notRemoverPopup

The script removes it from the page if it is present.

Removes Fixed Overlays

Many websites display fullscreen overlays using CSS such as:

position: fixed;
z-index: 9999;

The script searches for common overlay elements and removes them if they use fixed positioning.

Restores Browser Events

Websites sometimes intercept events like:

  • contextmenu

  • copy

  • cut

  • paste

  • selectstart

  • mousedown

  • mouseup

  • dragstart

The script stops these interception handlers from taking priority, allowing the browser's default behavior to work normally in many cases.


How to Run the Script

  1. Open the desired webpage.

  2. Press F12 to open Developer Tools.

  3. Select the Console tab.

  4. Paste the JavaScript code.

  5. Press Enter.

The changes apply only to the current page and disappear after a refresh.


Limitations

This technique only affects client-side JavaScript and CSS. It does not bypass server-side protections, authentication, or access controls. Some websites may also reload or recreate interface elements dynamically, requiring the script to be run again.

For developers, students, and power users, browser-side JavaScript can be a useful way to inspect how a page behaves and to restore standard browser functionality during debugging or testing. Understanding how event listeners, CSS properties, and DOM manipulation work is also a great way to learn more about modern web development.

If you frequently use this type of script, consider turning it into a userscript with extensions such as Tampermonkey so it can run automatically on pages where you have permission to use it.



(() => {
    "use strict";

    function hidePopup() {
        const popup = document.getElementById("notRemoverPopup");
        if (popup) {
            popup.remove(); // oppure: popup.style.display = "none";
        }
    }

    function enableRightClick() {
        document.oncontextmenu = null;
        document.onmousedown = null;
        document.onmouseup = null;
        document.onclick = null;

        document.addEventListener("contextmenu", e => {
            e.stopImmediatePropagation();
        }, true);

        document.addEventListener("mousedown", e => {
            if (e.button === 2) {
                e.stopImmediatePropagation();
            }
        }, true);

        document.querySelectorAll("*").forEach(el => {
            el.oncontextmenu = null;
            el.onmousedown = null;
            el.onmouseup = null;
            el.onclick = null;
        });
    }

    hidePopup();
    enableRightClick();

    new MutationObserver(() => {
        hidePopup();
    }).observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log("Popup  off.");
})();


https://www.examtopics.com/discussions/microsoft/view/94167-exam-az-204-topic-1-question-33-discussion