The thing is that tail.next is head.next, since at the init block we initialized tail = head, therefore I can't understand why in the append() method we can't do head.next = newNode instead of tail.next = newNode.
Your proposal would work the first time it executes, but as the next statement moves tail to reference the new node by doing tail = newNode, the equivalence of head and tail no longer holds. And so if append() is called again, head.next should no longer be touched.
It may help to visualise this. Let's say we start in a scenario where we have a list myLinkedList, and it has already one node:
myLinkedList
↓
┌────────────┐
│ tail: ──────────┐
│ head: ────────┐ │
│ length: 1 │ │ │
└────────────┘ │ │
▼ ▼
┌────────────┐
│ value: 1 │
│ next: null │
└────────────┘
As you correctly indicate, head == tail at this stage. So now, we call append(2). The first action is calling the Node constructor and assigning that reference to the local variable newNode:
myLinkedList
↓
┌────────────┐
│ tail: ──────────┐
│ head: ────────┐ │
│ length: 1 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: null │ │ next: null │
└────────────┘ └────────────┘
Then tail.next = newNode is executed, which indeed has the same result as doing head.next = newNode:
myLinkedList
↓
┌────────────┐
│ tail: ──────────┐
│ head: ────────┐ │
│ length: 1 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: ─────────│ next: null │
└────────────┘ └────────────┘
But then we do tail = newNode, which makes tail different from head:
myLinkedList
↓
┌────────────┐
│ tail: ───────────────────────┐
│ head: ────────┐ │
│ length: 2 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: ─────────│ next: null │
└────────────┘ └────────────┘
The length is also updated (above). It is clear that on a next execution of append(), we want the next member of the second node to be updated, not of the first node, and so it must be tail.next that gets assigned the new node's reference. Let's say we call append(3), then after we have created a new node and assigned it to newNode we get this:
myLinkedList
↓
┌────────────┐
│ tail: ───────────────────────┐
│ head: ────────┐ │
│ length: 2 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 2 │
│ next: ─────────│ next: null │ │ next: null │
└────────────┘ └────────────┘ └────────────┘
If now we would do head.next = newNode we would not get the desired effect:
myLinkedList
↓
┌────────────┐
│ tail: ───────────────────────┐
│ head: ────────┐ │
│ length: 2 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ────────┐ │ next: null │ ┌─│ next: null │
└────────────┘ │ └────────────┘ │ └────────────┘
└────────────────┘ (Wrong!)
This is wrong. That next reference in the first node should not have changed. Instead, we want the tail node to get its next reference updated from null to newNode:
myLinkedList
↓
┌────────────┐
│ tail: ───────────────────────┐
│ head: ────────┐ │
│ length: 2 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ─────────│ next: ─────────│ next: null │
└────────────┘ └────────────┘ └────────────┘
And now finally, the tail reference will be updated to point to the new node that is now really the tail. And the length updates as well:
myLinkedList
↓
┌────────────┐
│ tail: ─────────────────────────────────────────┐
│ head: ────────┐ │
│ length: 3 │ │ │
└────────────┘ │ │ newNode
▼ ▼ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ─────────│ next: ─────────│ next: null │
└────────────┘ └────────────┘ └────────────┘
We can now see that head.next should not be changed by append ever again. It should only happen when the second node is added. The common principle is that the last node in the list should get its next member updated. In one particular case that last node happens to be head, but that is just coincidence. We can better rely on updating tail.next, as that is guaranteed to be an update in the tail node, where null will be replaced with a reference to the new node.
I hope this clarifies it.