100% of your infrastructure is represented and managed in code via Terraform, with exception to application secret values. Additionally, it is organized with industry best-practices to cleanly isolate environments, minimize blast radius, and always maintain referential integrity. The remote state data is maintained in S3 such that multiple team members can work together without conflict.
Shared infrastructure is maintained in its own GitHub repo. This typically includes VPCs, security groups, routing tables, etc. Depending on the needs of your application(s), it may also include databases, S3 buckets, and any other resources that are not owned by a single application.
Each application typically has a lot of infrastructure resources that are specifically used and owned by it, such as databases, S3 buckets, load balancers, ECS services, DNS entries, etc. These resources are kept in the same GitHub repo as the application.
Within the application infrastructure are resources that specifically define the deployment, such as ECS tasks, container definitions, auto-scaling policies, etc. These are managed in such a way that when they are changed in code, the changes affect the deployment of the pull request (PR) in which they are made, and then in the production deployment after the PR is merged
For example, if the CPU and memory requirements for an application need to change, then simply modifying those Terraform values in an application repo PR will cause its preview deployment to use them, and then in the production deployment after the PR is merged.
The same is true for the Dockerfile that defines the application container. For example, if the Docker base image, software packages / versions, etc. are changed in a PR, they will be reflected in its preview deployment, and then in the production deployment after the PR is merged.