There are lots of ways around this problem. We have a tool that wraps our terraform runs, and rewrites the
backend.tf file and clears out .terraform as necessary when switching between tfvars files.
But the model I've come to think is slightly safer is to put all your actual Terraform resources, providers, etc into a module, and then for each environment (dev/prod/whatever), have a separate project that calls that module with all the parameters you need.
In terms of AWS and S3 themselves, we've had great luck with assuming IAM roles, which can be done across accounts, and which is integrated very very well into Terraform (better than it is with AWS's own tools).
The setup is something like this:
We have an AWS account that is _only_ for IAM users/groups/policies. All our ops team have individual IAM users in this account. Then in our other accounts for dev and our various applications, we set up IAM roles with whatever level of access we need, and grant the ability to assume those roles to the IAM-only AWS account. Then in the IAM-only account, we add users to groups that grant permission to assume the particular roles we created in other accounts. So for our core ops team, they can assume any of the remote roles. But for engineering teams, they can assume only roles which grant them access they need for their particular app.
Then in the AWS provider you can specify a role to assume before running (parameterize this if you are using the module-oriented config I mentioned), and in the remote state configuration you can specify a different role potentially in a different account, to assume when reading and writing the remote state.
Its' hard to explain, but it works completely transparently, and it feels pretty magical. With the modular setup described, we have some projects that actually span multiple AWS accounts, and you just pass which account you want to each module invocation and Terraform takes care of all the details of assuming the roles, and using the correct credentials for each resource. Then all the state for all those resources gets written via the different role to the bucket in an entirely other account. It's pretty great. Takes a bit of finagling to set up, but it's worth the effort.