The difference here is in control. The carpenter has full control on which task they take, whereas the developer hasn’t. A developer is forced to work at a bad product that they know is gonna fail, but the carpenter can refuse to take on a job that they know is gonna cost them more then the customer is willing to pay. For the developer, after their project inevitably fails, it will probably cost them their job, despite the failure not being their fault. Given the option, the developer would have voted against the project, the carpenter can simply refuse it. Upper management usually never takes the responsibility of failure, opting instead to mass layoffs. Shifting the cost of failures onto their exploited workers.
Instead of looking at the house the carpenter builds, look at the carpenter as an enterprise. If the carpenter grows in skill, and is able to take on more complicated jobs, they are able to charge more. The carpenter’s enterprise grows in value, which means more pay for the carpenter them self.
This is not true of the developer. The developer might be able to demand more pay, but they are at the mercy of their upper management to relay that to the owners of the business, who might see more value in exploiting the worker for more profit for them selves. Being able to collectively bargain through a union the developer might approach the freedom of their enterprise as the carpenter, but it is still not nearly the same level as if they had direct control of the business, like the carpenter does.