Sam Na writes practical guides on AI-assisted productivity, code troubleshooting, and dependable digital workflows. RoutineOS helps readers use AI tools with greater clarity while keeping their everyday systems understandable and reliable.
You can fix coding errors with AI more effectively when you give the assistant the actual failure, ask it to explain the cause, and check its proposed repair against real results. ChatGPT, Claude, Gemini, Grok, and GitHub Copilot can all support that process, but a convincing explanation is not the same as a verified fix.
You have been working on a small Python script that summarizes the time spent on different tasks. It has worked with the sample data you entered yourself. Then you import a new file and the script suddenly stops with a TypeError.
You paste the error into ChatGPT, and it suggests converting the input to a number. That sounds reasonable, so you make the change. The exception disappears, but you are still unsure whether the script will handle blank cells, unexpected values, or a different export tomorrow.
This is a familiar problem when debugging with AI. The assistant may identify a genuine coding mistake without knowing the rules your program is supposed to follow. It can explain what caused an exception, yet still propose a change that silently accepts bad data or alters behavior you wanted to preserve.
The solution is to treat AI as a debugging partner rather than an authority on the correct result. Give it enough evidence to investigate, ask for a small and justified repair, and use your own execution environment to decide whether the problem is resolved.
A useful AI debugging answer does three things: it identifies the likely cause, explains why a particular change addresses that cause, and gives you a way to check the result.
This guide explains how to prepare error messages and execution logs, use five popular AI tools, compare their suggestions, and verify fixes for Python and JavaScript. We will work through two different failures: a Python exception caused by inconsistent input types and a JavaScript logic bug that changes data without producing an error message.
Both examples are deliberately small so you can follow the reasoning and run the checks. They also reveal why the same debugging question can require different approaches depending on what the code actually does.
Identify the failure before asking AI to fix it
The first step in AI-assisted debugging happens before you open a chatbot. You need to establish what failed, where you observed the failure, and what the program should have done instead.
Without those details, even a capable coding assistant must fill in missing information. It may give you a familiar answer to a problem that merely resembles yours. A short investigation on your own helps prevent that mistake.
Separate exceptions, incorrect output, and environment failures
Not every coding problem produces an exception. Some programs stop with a traceback, while others finish normally and return the wrong result. A third group fails because the surrounding environment is different from the one in which the code previously worked.
These distinctions matter because each type of failure points toward different evidence. An exception may identify the operation that could not complete. Incorrect output requires comparing actual behavior with a specific expectation. An environment problem may depend on installed packages, file paths, configuration, or permissions.
Python raises a TypeError, JavaScript throws an exception, or an operation fails because a required value or resource is unavailable.
Start with the exact error message, relevant stack trace, and the input being processed when it occurred.
A report has the wrong total, a filter selects unexpected records, or a function changes an input that should remain untouched.
Start with a small example showing the actual output and the required result.
A package is unavailable, an expected configuration value is missing, or a script runs under a different interpreter.
Check the runtime, dependency installation, working directory, and relevant settings before rewriting the program.
An API response has changed, a file contains an unexpected field, or one function passes the wrong shape of data to another.
Inspect the actual values at the boundary where the components communicate.
You do not need to classify a problem perfectly before seeking help. The purpose of this first pass is to avoid asking an assistant to solve an undefined issue. A precise observation is more valuable than an impressive-sounding diagnosis.
Read the traceback or execution log from the point of failure
In Python, a traceback usually identifies the sequence of calls that led to an exception. The exception type and message appear near the end, while the preceding frames help locate the operation that failed.
That does not always mean the last line of code is where the underlying mistake began. A function might receive a string where it expected an integer because an earlier import step preserved values as text. The arithmetic operation exposes the mismatch, but the data boundary explains how it happened.
JavaScript stack traces serve a similar purpose. They can show the functions involved in a thrown error, although the exact wording and presentation depend on the runtime. Browser consoles and Node.js terminals may also include information about failed requests, rejected promises, or warnings.
Read those messages before changing code. When a log is long, extract the first relevant failure, the operation involved, and enough surrounding context to explain what the program was processing. Do not discard earlier errors that may have caused the final failure.
Reproduce the problem with a small, safe example
A reproducible example is a small input or sequence of actions that reliably produces the same unwanted behavior. It helps you determine whether a proposed fix addresses the actual defect or simply makes one demonstration look successful.
Suppose a script fails while reading hundreds of activity records. You may only need two records to expose the problem: one containing a numeric value and another containing a number stored as text. If those records trigger the same exception, the smaller example becomes a useful debugging tool.
Keep the original input available while reducing the example. Removing too much context can change the behavior you are trying to understand. A useful reproduction must still demonstrate the failure, not merely contain code that looks related.
Before asking AI to debug your code, identify the observable failure, preserve the relevant error evidence, and define the correct result. A small reproduction gives every proposed fix a meaningful starting point.
Build a debugging prompt that produces useful answers
A vague prompt such as "fix my Python code" gives an assistant considerable freedom to guess what is wrong. It may rewrite the function, introduce a new dependency, or change how invalid values are handled without knowing whether those changes fit your project.
A better approach is to provide a compact debugging brief. It should describe the intended behavior, relevant code, actual failure, known environment, and limits on acceptable changes.
Give the assistant a complete but focused debugging brief
The most important information is not necessarily the longest code sample. An assistant needs to see the operation involved in the failure, the values entering that operation, and the result that would count as correct.
For an input-processing bug, include the source of the data and its expected format. For an import problem, mention whether the script runs directly, inside a scheduled job, or through another application. Add exact version information when it affects the issue.
You should also describe what must remain unchanged. If a function currently rejects invalid records, do not let the assistant silently replace that policy with skipping every questionable value. The choice could make the error disappear while hiding a data-quality problem.
This structure works with ChatGPT, Claude, Gemini, Grok, and Copilot, although the way you provide files or code context differs by product. It also makes responses easier to compare because the requested outcome remains consistent.
Share enough code without exposing private information
Debugging context can contain sensitive material even when the code itself appears ordinary. Terminal output may include account identifiers, file paths, access tokens, private records, or request headers.
Before pasting information into an external AI service, remove secrets and replace personal values with artificial examples. Preserve the types, field names, and relationships required to reproduce the bug. Changing a numeric string into an actual integer, for example, could accidentally erase the problem.
Also consider whether the code belongs to an employer or client. A private repository may be subject to access rules, contractual restrictions, or organizational AI policies. The availability of an upload feature does not automatically authorize sharing its contents.
Remove passwords, API keys, session tokens, personal records, and confidential source material before sharing debugging context. Review the tool's data controls and your project's policies before uploading files.
Ask for a cause before asking for replacement code
If the first response contains an entirely rewritten function, you may struggle to identify which change actually fixed the problem. Several changes introduced at once can also create new behavior that has nothing to do with the original failure.
Ask the assistant to identify the failing expression and explain the values involved before it proposes a patch. This makes the response easier to evaluate, particularly when you are learning Python or JavaScript.
Then request a minimal change. A useful proposal should state what condition it handles, what it deliberately leaves untouched, and which new assumptions it introduces. If the explanation depends on an unverified guess, gather more evidence rather than accepting the patch immediately.
The assistant has no defined correctness criteria and may replace working behavior while attempting to solve the problem.
The assistant receives an observed failure, a required outcome, and explicit limits on acceptable changes.
When a debugging answer sounds certain but the evidence is incomplete, ask which observation would distinguish its explanation from another possible cause. That question often leads to a better investigation than requesting another immediate rewrite.
A good AI debugging prompt supplies the intended behavior, real failure, relevant context, and change boundaries. Ask for evidence-based reasoning and a small patch before accepting replacement code.
Debug with ChatGPT, Claude, Gemini, Grok, and Copilot
The five tools can all assist with coding errors, but they do not provide identical working environments. Some interactions begin with a standalone conversation. Others happen inside an editor where relevant code and project files may already be available.
That difference matters more than choosing a favorite model in advance. The useful question is whether the assistant has enough trustworthy context to explain the failure and propose a change you can verify.
Use conversational AI to investigate the error
Standalone chat interfaces are useful when you have a compact example, an error message, and a clearly defined question. They let you concentrate on the reasoning before touching the project.
You can request an explanation of a traceback, ask why a particular value is invalid, or compare two possible fixes. The assistant's access to files, execution environments, or other tools depends on the product and your available features.
ChatGPT is useful for breaking down an unfamiliar exception, tracing the flow of values, and discussing why one change may be preferable to another.
Begin with a sanitized traceback and a small function. Ask it to identify the failing operation before requesting modified code.
If code execution is available in your chosen workflow, inspect what actually ran. Otherwise, treat the response as a proposal to test in your own environment.
Claude can examine supplied code and explain potential causes. Claude Code adds a project-oriented workflow in which it can inspect files, suggest edits, and work with commands subject to its permissions and settings.
Ask it to trace where an unexpected value entered the function and identify which files would need to change.
Review proposed changes and executed commands rather than assuming that an agent's completion message establishes correctness.
Gemini can discuss code in a conversational setting. Gemini Code Assist also provides IDE-based assistance for explaining code and proposing corrections.
In supported environments, its code transformation workflow can suggest a fix and present changes for review. Available features depend on the edition and setup.
Supply the error context and examine the proposed diff before applying it. A convenient quick fix still needs local verification.
Grok can analyze code and help reason about a supplied error. It is also useful as a separate conversational perspective when an initial diagnosis is uncertain.
Give it the same reduced example and ask which operation causes the failure, what evidence supports that conclusion, and which assumptions remain.
Do not interpret agreement with another assistant as proof. Both responses may rely on the same incomplete information.
Use GitHub Copilot when the error is tied to your editor
GitHub Copilot is particularly relevant when you are already working inside a supported development environment. Instead of copying a large file into a separate conversation, you can ask questions about selected code or project context.
Copilot Chat supports code explanation, fix suggestions, and other programming assistance. Its available commands and interaction modes vary across editors and configurations.
For example, GitHub documents
/fix
as a way to request a correction
for selected code in supported
Copilot Chat environments.
The official
GitHub Copilot Chat cheat sheet
explains common commands and
their context requirements.
The advantage is convenience, not automatic reliability. An editor assistant may know which function you selected, but it may still lack the real input that triggered the problem or the business rule the code must respect.
When Copilot proposes an edit, inspect the changed lines and run the affected code yourself. If the suggestion modifies unrelated files, ask why those changes are necessary before accepting them.
GitHub Copilot and Gemini Code Assist can help you work near the relevant source code. Claude Code can also investigate project files in its supported workflow. The amount of context each assistant sees depends on the active setup.
For a small standalone exception, a chat conversation may be enough. For a failure spread across several modules, project-aware tools can reduce the effort of gathering the relevant files.
Compare diagnoses by evidence rather than confidence
Using several AI assistants can help when one suggestion is incomplete or introduces a questionable change. However, sending the same prompt to five models does not automatically produce five independent proofs.
Start with one assistant and request a specific diagnosis. If the answer leaves an important uncertainty, ask another tool to inspect the original evidence without showing it the first answer. This reduces the chance of simply repeating an earlier assumption.
Compare the responses according to their proposed causes, affected operations, assumptions, and verification steps. A shorter, more precise change may be more appropriate than an ambitious rewrite that touches unrelated code.
This comparison is most valuable when the assistants disagree about the same evidence. One model may blame data conversion, while another suspects a wrong function argument. You can often settle the disagreement by inspecting the actual value entering the failing operation.
Choose an AI debugging tool according to the context it can access and the task you need it to perform. Compare suggested causes against observable evidence, not the number of assistants that agree or the confidence of their explanations.
Fix a Python TypeError with AI assistance
Let us apply the process to a simple Python activity summary. The program receives records containing the number of minutes spent on a task. It should add those durations and return the total.
The original implementation works when every value is already an integer. A problem appears when imported data contains a duration stored as a string, which is common when values originate in a text-based file or external response.
Reproduce the exception and identify the type mismatch
The following function assumes
that the value under
minutes
can be added directly to an
integer total.
def total_minutes(rows):
return sum(row["minutes"] for row in rows)
activity = [
{"minutes": "25"},
{"minutes": 30},
]
print(total_minutes(activity))
When this example runs, Python
attempts to add an integer
accumulator to the string
"25".
Those operands are not compatible
with the addition being performed,
so the function raises a TypeError.
The important part of the resulting
message is the mismatch between
an integer and a string. The problem
is not that the characters
25
are meaningless. It is that
the program has not defined
how text values should become
numeric durations.
Expected: A total of 55 minutes.
Observed: A TypeError when the summation encounters a duration stored as text.
Likely correction: Define how imported values are validated and converted before arithmetic.
If you asked an AI code debugger
to fix this example, it might
suggest wrapping every value
in int().
That could handle the displayed
numeric string, but it does not
fully describe the valid input
your program should accept.
Before applying that suggestion, decide how the function should behave when the value is blank, negative, fractional, missing, or not numeric at all.
Define the data rule before accepting the AI fix
For this example, we will accept non-negative whole numbers supplied either as Python integers or as strings containing ASCII digits. Surrounding whitespace in a numeric string is allowed because text exports may include extra spaces.
We will reject boolean values, negative durations, decimals, missing values, and strings containing units or other unexpected characters. Invalid input will raise a clear exception identifying the affected row.
This is a deliberate data policy, not a universal rule for time tracking. A different application might allow fractional minutes or choose to report invalid records separately. The important part is that the rule is explicit before the AI changes the code.
A blanket conversion may accept or reject values in ways the program owner never intended.
It also makes it harder to distinguish ordinary imported text from genuinely invalid data.
Define acceptable inputs, convert only those values, and report unsupported records clearly.
The exception disappears for valid numeric strings without silently changing the intended data policy.
The corrected version below uses a small loop to make each decision visible. It also keeps the error message specific enough to identify which row needs attention.
import re
def total_minutes(rows):
total = 0
for position, row in enumerate(rows, start=1):
value = row.get("minutes")
if isinstance(value, bool):
raise ValueError(
f"Row {position}: minutes must be "
"a non-negative whole number"
)
if isinstance(value, int):
minutes = value
elif (
isinstance(value, str)
and re.fullmatch(r"[0-9]+", value.strip())
):
minutes = int(value.strip())
else:
raise ValueError(
f"Row {position}: minutes must be "
"a non-negative whole number"
)
if minutes < 0:
raise ValueError(
f"Row {position}: minutes must be "
"a non-negative whole number"
)
total += minutes
return total
The function now separates input validation from the arithmetic operation. Valid numeric strings are converted, while unsupported values produce an exception with a predictable explanation.
Notice the explicit boolean check. In Python, boolean values are subclasses of integers, so a simple integer type check would otherwise accept them. That would be inappropriate for the duration rule defined in this example.
The use of
re.fullmatch()
also prevents the function
from accepting strings that
merely begin with a number.
A value such as
"25min"
must be handled deliberately
rather than being treated as
a valid integer without review.
This implementation assumes each row is a dictionary. If a real importer can produce completely different row shapes, that boundary deserves its own validation. There is no need to add speculative handling for every imaginable structure to demonstrate the original fix.
Run the repaired code and check rejected inputs
A corrected function is still only a proposal until you execute it. Begin with the exact input that caused the original failure.
activity = [
{"minutes": "25"},
{"minutes": 30},
]
print(total_minutes(activity))
# Expected output: 55
Next, check behavior that could be accidentally changed by an overly permissive conversion. The following small verification script checks valid values, empty input, and several rejected representations.
assert total_minutes([
{"minutes": "25"},
{"minutes": 30},
]) == 55
assert total_minutes([]) == 0
for bad_value in [
"25min",
None,
True,
-5,
2.5,
]:
try:
total_minutes([
{"minutes": bad_value}
])
except ValueError:
pass
else:
raise AssertionError(
f"Invalid value was accepted: {bad_value!r}"
)
print("Verification completed")
The corrected Python implementation was checked locally with the displayed valid cases and five invalid-value examples. All seven checks completed as expected in that local verification.
That result supports the behavior demonstrated here. It does not establish that a separate CSV importer, database connection, or real scheduling process is correct. Those components were not part of the example.
A useful follow-up question for the AI assistant is: "Which valid inputs would this correction reject, and which invalid inputs might still pass?" That request examines the boundaries of the fix without turning the conversation into an unrelated rewrite.
The Python error came from adding incompatible data types. The repair works by defining acceptable input, validating it, and converting numeric strings before arithmetic. Local execution confirms the demonstrated behavior; the AI explanation alone would not have done so.
Find a JavaScript bug that produces no exception
Some coding errors are harder to recognize because the program continues running. JavaScript can produce a valid-looking output while changing data that another part of the application still needs.
Consider a small automation that sorts recorded work sessions by duration. The sorting function should return a new ordered list without changing the original sequence of recorded entries.
Detect an unexpected side effect in array sorting
The following code appears straightforward. It sorts three duration values in ascending numerical order.
function sortedDurations(minutes) {
return minutes.sort((a, b) => a - b);
}
const original = [45, 10, 30];
const sorted = sortedDurations(original);
console.log(sorted);
console.log(original);
Both logs display the sorted sequence. The first result is what the function was supposed to produce, but the second reveals a problem: the caller's original array has also been reordered.
JavaScript's
Array.prototype.sort()
modifies the array on which
it is called. That behavior
is documented in
MDN's Array.sort() reference.
The method is doing what
JavaScript specifies, even
though it violates our
function's intended contract.
This is a useful example of why an AI debugger needs expected behavior, not only an error message. There is no exception to paste into the chat. The bug becomes visible only when you compare the input before and after calling the function.
Expected output: [10, 30, 45]
Required original input: [45, 10, 30]
Actual side effect: The original array is also reordered by the sorting operation.
Suppose another part of the automation later displays sessions in the order they were recorded. It may now present them in duration order instead. The initial sorted report looks correct, but a different feature receives data that has changed unexpectedly.
Ask AI to trace the mutation and propose a narrow fix
When asking ChatGPT, Claude, Gemini, Grok, or Copilot about this behavior, explain that the function must preserve its input. Otherwise, an assistant might consider the original implementation completely acceptable.
A narrow correction creates a shallow copy of the array before sorting. For this example, the elements are numbers, so a copied array is sufficient to preserve the original sequence.
function sortedDurations(minutes) {
return [...minutes].sort((a, b) => a - b);
}
The spread expression creates a new array. Sorting that new array does not reorder the array supplied by the caller. The numeric comparison remains unchanged.
There is another valid
approach: modern JavaScript
also provides
toSorted(),
which returns a sorted copy.
Its availability depends
on the runtime version,
so the spread-and-sort
version is an understandable
option when you want
straightforward compatibility
with environments that
support standard spread syntax.
The shallow-copy detail matters if the array contains objects. Copying an array does not clone the objects inside it. A different implementation could still mutate shared object properties. Our example uses numbers and does not make that claim.
The result is numerically ordered, but the input order is lost.
That side effect can affect later operations that reuse the same array.
The returned values remain numerically ordered.
The caller's array keeps its original sequence of numbers.
Verify both the output and the original input
Checking only the returned array would miss the defect. The verification must test both sides of the contract: the result is sorted and the original data remains in its previous order.
The following script uses Node.js's built-in assertion module. It does not require a third-party testing library for this small demonstration.
const assert = require("node:assert/strict");
function sortedDurations(minutes) {
return [...minutes].sort((a, b) => a - b);
}
const original = [45, 10, 30];
assert.deepEqual(
sortedDurations(original),
[10, 30, 45]
);
assert.deepEqual(
original,
[45, 10, 30]
);
assert.deepEqual(
sortedDurations([]),
[]
);
console.log("Verification completed");
The corrected function was checked locally with Node.js. The three displayed assertions passed: the result was sorted, the input remained unchanged, and an empty array returned an empty array.
This result verifies the specific function and inputs shown here. It does not establish that every object collection or external application will preserve all of its state.
This example also shows how to evaluate AI-generated debugging suggestions. The best answer is not necessarily the one containing the most code. It is the one that addresses the demonstrated side effect while preserving the function's intended result.
JavaScript bugs can exist without exceptions. When the problem involves unintended mutation, verify the original input as well as the returned value. A small copy-before-sort change resolves the demonstrated defect without rewriting unrelated logic.
Verify the repair and reject risky AI suggestions
Once an AI assistant proposes a plausible fix, the debugging task is not quite finished. You need to decide whether the change addresses the supported cause, preserves important behavior, and works in the environment where the program runs.
The verification process does not need to become complicated for every small script. It does need to be specific enough to detect the original problem and obvious new mistakes.
Review the code diff before running the repaired version
A diff shows the lines changed between the original and proposed implementation. Reading it helps you recognize modifications that are unrelated to the reported failure.
Suppose you ask an AI assistant to repair one invalid-value check. It responds by changing the entire input parser, renaming several functions, and introducing a package. Those changes may be defensible in a separate refactoring task, but they require their own explanation and verification.
For a small bug, prefer a patch whose scope you can understand. The goal is not to minimize line count at any cost. It is to avoid changing more behavior than the evidence requires.
Check the original failure, neighboring behavior, and side effects
Begin with the input that caused the original problem. If it still fails, the proposed fix has not resolved the demonstrated issue. If it succeeds, check one or two nearby conditions that the new code could affect.
The Python example needed to accept numeric text without accepting invalid duration values. The JavaScript example needed to produce a sorted result without modifying its input. Each required more than checking whether the program completed successfully.
For scripts that read files or call services, keep verification separate from production data wherever possible. A debugging session should not accidentally overwrite important documents, send real notifications, or modify accounts while testing a proposed code change.
Record the environment when it matters. A fix that works under one Python version or Node.js configuration may depend on features not available in another installation. Use the actual target environment for final verification.
Recognize when the problem is outside the code itself
Sometimes repeated attempts to fix a function fail because the source code is not the underlying problem. A scheduled job may use a different configuration, an API may reject credentials, or a deployed environment may run an older version.
If the code passes your small local example but fails in the actual workflow, compare the inputs, runtime, configuration, and external dependencies. Ask the AI assistant which environmental difference could explain the observed result.
Do not repeatedly rewrite a working function because an integration failure happens nearby. First determine whether the function receives the same values and whether the surrounding operation succeeds.
Do not run unfamiliar AI-generated commands with elevated privileges simply to make an error disappear. Inspect commands that delete files, change permissions, install packages, or access remote systems before approving them.
A reasonable stopping point is reached when the original failure has been reproduced, the cause is supported by evidence, the patch is understood, and the relevant checks pass in the target environment.
For a personal script, that may involve only a few commands. For code that handles important data or external actions, it may require broader testing and review by someone familiar with the system.
An AI assistant can propose a reason, a patch, and a test. Only the actual execution evidence can show what the code did under the conditions you checked. Keep those two roles separate.
Accept an AI-generated fix only after reviewing its scope and checking the original failure, neighboring behavior, and possible side effects. If local results disagree with the real workflow, investigate the environment before rewriting more code.
Frequently Asked Questions
Conclusion: make AI debugging a process you can trust
AI assistants can make coding errors easier to understand. They can explain unfamiliar exceptions, identify suspicious operations, compare approaches, and suggest corrections that would otherwise take longer to investigate manually.
The quality of the result still depends on the evidence you provide and the behavior you expect. A model cannot reliably infer every data rule, environment condition, or side effect from a single error message.
The Python example showed how an input type mismatch can be repaired without losing control of valid and invalid values. The JavaScript example showed that a function can produce the correct result while unexpectedly changing its input. Those problems required different verification checks because their failure conditions were different.
ChatGPT, Claude, Gemini, Grok, and GitHub Copilot are most useful when they help you understand that distinction. You do not need to consult every tool for every error. Begin with one appropriate assistant, gather more evidence when necessary, and seek another perspective when a proposed cause remains uncertain.
The next time a script fails, save the actual error, reduce the example, and write the required result before requesting a patch. Then review the suggested change and execute a focused verification check. This small habit makes debugging more predictable without requiring an elaborate development environment.
Choose a small Python or JavaScript problem you can reproduce safely. Record the actual failure and the result you expect. Give that evidence to one AI assistant using the debugging prompt from this guide, then review its proposed correction. Run the changed code yourself and check both the original problem and one nearby behavior. Keep only the changes you can explain and verify.
Sam Na writes RoutineOS guides on AI-assisted productivity, practical coding workflows, and dependable personal automation. His articles focus on helping readers understand how digital tools work, investigate problems methodically, and make informed decisions about the systems they use every day.
This guide provides general information and educational coding examples. The appropriate debugging approach depends on your programming environment, data, project requirements, and the consequences of each code change.
Run unfamiliar code with safe sample data and inspect suggested commands before executing them. For important production systems, sensitive information, or changes that affect other people, consult current official documentation and a qualified developer or security professional when appropriate.
Official documentation describing common Copilot Chat commands, code explanations, fix requests, and editor context.
GitHub Copilot Chat documentationOfficial guidance on using Gemini Code Assist for code transformations, explanations, and proposed corrections inside supported IDEs.
Gemini Code Assist coding guidanceJavaScript reference explaining how array sorting works, why it changes the original array, and how to create a sorted copy.
JavaScript Array.sort() reference