Melt the butter in your Dutch oven over medium heat, then add the diced onion, carrots, and celery. Cook for about 5 minutes, stirring occasionally, until the onion turns soft and translucent. This step builds the flavor foundation for the entire soup.
Sprinkle the flour directly over the softened vegetables and stir constantly for about a minute. This coats the vegetables and cooks off the raw flour taste. You'll notice the mixture turning slightly golden and pasty, which is exactly what you want.
Slowly pour in the chicken broth while whisking to avoid lumps, then add the cubed potatoes. Bring the pot to a gentle boil, then reduce the heat and let it simmer uncovered for about 15 minutes, or until the potatoes turn fork-tender.
Once the potatoes are soft, lower the heat and pour in the milk gradually, stirring the whole time. Let the soup warm through for a couple of minutes without letting it boil, since boiling milk can cause it to separate and turn grainy.
Turn the heat down to low and add the shredded cheddar cheese in small handfuls, stirring after each addition until fully melted. Adding it gradually like this keeps the soup smooth instead of clumpy or oily.
Stir in the diced ham and let it heat through for 3 to 5 minutes. Taste the soup now and adjust the salt and pepper, keeping in mind that the ham already carries a good amount of saltiness.
Ladle the soup into bowls while it's hot and top with a sprinkle of extra cheddar or chopped green onions. The soup will continue to thicken slightly as it sits, so serve it right away for the creamiest texture.