Writing a Custom Controller is standard Apex development with Visualforce-specific patterns for getters, setters, and action methods. The work is mostly about understanding the page lifecycle.
- Define the Apex class
From Setup or Developer Console, create a new Apex class. Make it public. Add public properties for data the page needs, getter methods for computed values, and action methods for button handlers.
- Reference the controller from the Visualforce page
Set the apex:page controller attribute to the class name. Reference properties through the merge syntax and action methods through commandButton actions.
- Handle form input
Add public setter properties for each form field. Salesforce auto-populates them on form submit. Validate values inside the action method, not the setter, to avoid surprise side effects.
- Manage view state
Mark large properties as transient to keep them out of view state. Query results that are needed on the current request but not on the next can stay transient.
- Write Apex tests
Build a test class that instantiates the controller, sets input properties, calls action methods, and asserts state transitions and PageReference returns. Aim for 75 percent code coverage minimum.
- Migrate to LWC when feasible
For pages that get heavy use or face performance issues, rebuild as LWC with an Apex controller called through AuraEnabled. The modern stack is faster and more maintainable.
- View state is serialized on every page render. Large collections in public properties exhaust the 170 KB view state limit and cause runtime errors.
- Action methods that perform DML must respect Apex governor limits. The 150 DML statements per transaction limit can be reached fast on bulk operations.
- Setters fire on form submit in arbitrary order. Do not rely on order-of-setter execution for business logic.
- Custom Controllers do not work in Lightning Experience the same way as in Classic. Test in both contexts; some behaviors render differently.
- Custom Controllers cannot be deleted if they are referenced by a Visualforce page. Delete the page references first.