phony targets, variables, and pattern rules
Once you know the basic rule, three small features make Makefiles far less repetitive and less error-prone. A phony target is a rule whose name is an action, not a file (like clean or all); a variable lets you write a value once and reuse it; and a pattern rule writes one rule that applies to a whole family of files instead of copying it for each.
A phony target is one you declare with .PHONY: clean. Normally make clean works, but if a file literally named clean ever exists in the folder, Make would see it as up to date and refuse to run the recipe; marking it phony tells Make this name is an action to always run, never a file to check. A variable is written as CC = gcc or CFLAGS = -O2 -Wall and used as $(CC) or $(CFLAGS), so you set your compiler and flags in one place. A pattern rule uses % as a wildcard: the single rule '%.o: %.c' followed by a recipe says how to make any .o from the matching .c, with $< meaning the prerequisite (the .c) and $@ meaning the target (the .o). One pattern rule can replace dozens of near-identical rules.
Why it matters: these turn a Makefile from a wall of copy-pasted lines into something short and maintainable, where changing a compiler flag is a one-line edit. The catch is readability: variables like $@, $<, and $^ are terse 'automatic variables' that are powerful but cryptic to a newcomer, and over-clever Makefiles can become hard to debug. Used in moderation, though, they are exactly what keeps a real project's build sane.
CC = gcc CFLAGS = -O2 -Wall %.o: %.c # pattern rule: any .o from its .c $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean # clean is an action, not a file clean: rm -f *.o app
A variable, a pattern rule, and a phony target replace many repetitive lines.
If you forget .PHONY and someone creates a file named clean, make clean silently does nothing, because Make thinks the 'clean' file is already up to date. The phony declaration exists precisely to prevent this surprise.