SGF's internal structure is a tree of property lists.
This allows storing variations of the main line of play.
The tree is written in pre-order.
Here's a short sketch for an algorithm for writing a tree in pre-order:

Algorithm

Example tree

pre-order

WriteTree(Root)
End
WriteTree(Node)
Write(Node)
for each child of Node
WriteTree(child)
end for
end

Note the beginning of a new game tree in front of B[cc]
and the variation itself on the second line. There are two ")" at the end:
one for the variation gametree and one for the end of the main gametree
starting at FF[4].

The new game tree starts in front of W[hg]. Note that
there are three ")" at the end: one for the W[kl] variation, one
for the B[gg] variation, and one for the main gametree.

Common pitfalls:

Every variation has at least one node (see EBNF
definition)! That is, the smallest possible variation looks like
(;) - () is an error! Another example: removing all
properties from the "variation of a variation" example leads to:
(;;;(;;;;)(;;)(;;;(;;)(;))) and additionally removing unnecessary
nodes leads to (;(;)(;)(;(;)(;)))

Properties are part of a node, therefore (W[tt]) is an error.
Correct is (;W[tt])